blob: 31933e496fd99922c3624fb7c936be7ed606ad86 [file] [log] [blame]
Jonathan Hart472062d2014-04-03 10:56:48 -07001package net.onrc.onos.core.topology;
Jonathan Hart062a2e82014-02-03 09:41:57 -08002
Pavlin Radoslavov018d5332014-02-19 23:08:35 -08003import java.nio.ByteBuffer;
Yuta HIGUCHI8d762e92014-02-12 14:10:25 -08004import java.util.ArrayList;
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -07005import java.util.Arrays;
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -08006import java.util.Collection;
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -07007import java.util.Comparator;
Pavlin Radoslavov018d5332014-02-19 23:08:35 -08008import java.util.HashMap;
Yuta HIGUCHI8d762e92014-02-12 14:10:25 -08009import java.util.HashSet;
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080010import java.util.LinkedList;
Yuta HIGUCHI8d762e92014-02-12 14:10:25 -080011import java.util.List;
Pavlin Radoslavov018d5332014-02-19 23:08:35 -080012import java.util.Map;
Yuta HIGUCHI8d762e92014-02-12 14:10:25 -080013import java.util.Set;
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -070014import java.util.TreeSet;
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080015import java.util.concurrent.BlockingQueue;
Yuta HIGUCHIa536e762014-02-17 21:47:28 -080016import java.util.concurrent.CopyOnWriteArrayList;
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080017import java.util.concurrent.LinkedBlockingQueue;
Yuta HIGUCHI8d762e92014-02-12 14:10:25 -080018
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -070019import javax.annotation.concurrent.GuardedBy;
20
TeruU28adcc32014-04-15 17:57:35 -070021import net.floodlightcontroller.util.MACAddress;
Jonathan Hart6df90172014-04-03 10:13:11 -070022import net.onrc.onos.core.datagrid.IDatagridService;
23import net.onrc.onos.core.datagrid.IEventChannel;
24import net.onrc.onos.core.datagrid.IEventChannelListener;
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070025import net.onrc.onos.core.metrics.OnosMetrics;
26import net.onrc.onos.core.metrics.OnosMetrics.MetricsComponent;
27import net.onrc.onos.core.metrics.OnosMetrics.MetricsFeature;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070028import net.onrc.onos.core.registry.IControllerRegistryService;
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -070029import net.onrc.onos.core.util.Dpid;
Jonathan Hart23701d12014-04-03 10:45:48 -070030import net.onrc.onos.core.util.EventEntry;
Yuta HIGUCHI5c8cbeb2014-06-27 11:13:48 -070031import net.onrc.onos.core.util.SwitchPort;
32
Jonathan Hart062a2e82014-02-03 09:41:57 -080033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070036import com.codahale.metrics.Gauge;
37import com.codahale.metrics.Meter;
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -070038
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080039/**
Jonathan Harte37e4e22014-05-13 19:12:02 -070040 * The TopologyManager receives topology updates from the southbound discovery
41 * modules and from other ONOS instances. These updates are processed and
42 * applied to the in-memory topology instance.
Ray Milkey269ffb92014-04-03 14:43:30 -070043 * <p/>
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -080044 * - Maintain Invariant/Relationships between Topology Objects.
Ray Milkey269ffb92014-04-03 14:43:30 -070045 * <p/>
Yuta HIGUCHI765cd0d2014-02-06 12:46:41 -080046 * TODO To be synchronized based on TopologyEvent Notification.
Ray Milkey269ffb92014-04-03 14:43:30 -070047 * <p/>
Yuta HIGUCHIcb951982014-02-11 13:31:44 -080048 * TODO TBD: Caller is expected to maintain parent/child calling order. Parent
Yuta HIGUCHI1c700102014-02-12 16:30:52 -080049 * Object must exist before adding sub component(Add Switch -> Port).
Ray Milkey269ffb92014-04-03 14:43:30 -070050 * <p/>
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -080051 * TODO TBD: This class may delay the requested change to handle event
52 * re-ordering. e.g.) Link Add came in, but Switch was not there.
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080053 */
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070054public class TopologyManager {
Jonathan Hart062a2e82014-02-03 09:41:57 -080055
Yuta HIGUCHI80829d12014-02-05 20:16:56 -080056 private static final Logger log = LoggerFactory
Ray Milkey269ffb92014-04-03 14:43:30 -070057 .getLogger(TopologyManager.class);
Yuta HIGUCHIcd922f42014-02-11 18:59:11 -080058
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080059 private IEventChannel<byte[], TopologyEvent> eventChannel;
Jonathan Hart10a7e2b2014-02-21 18:30:08 -080060 public static final String EVENT_CHANNEL_NAME = "onos.topology";
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080061 private EventHandler eventHandler = new EventHandler();
62
Jonathan Harte37e4e22014-05-13 19:12:02 -070063 private final TopologyImpl topology = new TopologyImpl();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -070064 private TopologyEventPreprocessor eventPreprocessor;
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -070065 private CopyOnWriteArrayList<ITopologyListener> topologyListeners =
66 new CopyOnWriteArrayList<>();
Pavlin Radoslavov054cd592014-08-07 20:57:16 -070067 private CopyOnWriteArrayList<ITopologyListener> newTopologyListeners =
68 new CopyOnWriteArrayList<>();
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080069
Pavlin Radoslavov706add22014-02-20 12:15:59 -080070 //
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070071 // Metrics
72 //
73 private static final MetricsComponent METRICS_COMPONENT =
74 OnosMetrics.registerComponent("Topology");
75 private static final MetricsFeature METRICS_FEATURE_EVENT_NOTIFICATION =
76 METRICS_COMPONENT.registerFeature("EventNotification");
77 //
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070078 // Timestamp of the last Topology event (ms from the Epoch)
79 private volatile long lastEventTimestampEpochMs = 0;
80 private final Gauge<Long> gaugeLastEventTimestampEpochMs =
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070081 OnosMetrics.registerMetric(METRICS_COMPONENT,
82 METRICS_FEATURE_EVENT_NOTIFICATION,
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070083 "LastEventTimestamp.EpochMs",
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070084 new Gauge<Long>() {
85 @Override
86 public Long getValue() {
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070087 return lastEventTimestampEpochMs;
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070088 }
89 });
90 // Rate of the Topology events published to the Topology listeners
91 private final Meter listenerEventRate =
92 OnosMetrics.createMeter(METRICS_COMPONENT,
93 METRICS_FEATURE_EVENT_NOTIFICATION,
94 "ListenerEventRate");
95
96 //
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -070097 // Local state for keeping the last ADD Mastership Event entries.
98 // TODO: In the future, we might have to keep this state somewhere else.
99 //
100 private Map<ByteBuffer, MastershipEvent> lastAddMastershipEvents =
101 new HashMap<>();
102
103 //
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800104 // Local state for keeping track of the application event notifications
105 //
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700106 // - Queue of events, which will be dispatched to local listeners
107 // on next notification.
Yuta HIGUCHI703696c2014-06-25 20:36:45 -0700108
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700109 private List<MastershipEvent> apiAddedMastershipEvents =
110 new LinkedList<>();
111 private List<MastershipEvent> apiRemovedMastershipEvents =
112 new LinkedList<>();
Yuta HIGUCHI703696c2014-06-25 20:36:45 -0700113 private List<SwitchEvent> apiAddedSwitchEvents = new LinkedList<>();
114 private List<SwitchEvent> apiRemovedSwitchEvents = new LinkedList<>();
115 private List<PortEvent> apiAddedPortEvents = new LinkedList<>();
116 private List<PortEvent> apiRemovedPortEvents = new LinkedList<>();
117 private List<LinkEvent> apiAddedLinkEvents = new LinkedList<>();
118 private List<LinkEvent> apiRemovedLinkEvents = new LinkedList<>();
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700119 private List<HostEvent> apiAddedHostEvents = new LinkedList<>();
120 private List<HostEvent> apiRemovedHostEvents = new LinkedList<>();
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800121
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800122 /**
123 * Constructor.
124 *
Jonathan Harte37e4e22014-05-13 19:12:02 -0700125 * @param registryService the Registry Service to use.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800126 */
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700127 public TopologyManager(IControllerRegistryService registryService) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700128 this.eventPreprocessor =
129 new TopologyEventPreprocessor(registryService);
Yuta HIGUCHI80829d12014-02-05 20:16:56 -0800130 }
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -0800131
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800132 /**
Jonathan Harte37e4e22014-05-13 19:12:02 -0700133 * Get the Topology.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800134 *
Jonathan Harte37e4e22014-05-13 19:12:02 -0700135 * @return the Topology.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800136 */
Jonathan Harte37e4e22014-05-13 19:12:02 -0700137 Topology getTopology() {
138 return topology;
Pavlin Radoslavov6d224ee2014-02-18 16:43:15 -0800139 }
140
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -0800141 /**
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800142 * Event handler class.
143 */
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700144 class EventHandler extends Thread implements
Ray Milkey269ffb92014-04-03 14:43:30 -0700145 IEventChannelListener<byte[], TopologyEvent> {
146 private BlockingQueue<EventEntry<TopologyEvent>> topologyEvents =
147 new LinkedBlockingQueue<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800148
Ray Milkey269ffb92014-04-03 14:43:30 -0700149 /**
150 * Startup processing.
151 */
152 private void startup() {
153 //
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700154 // Read all topology state
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 //
Ray Milkey5df613b2014-04-15 10:50:56 -0700156 Collection<TopologyEvent> allTopologyEvents =
Ray Milkey269ffb92014-04-03 14:43:30 -0700157 eventChannel.getAllEntries();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700158 List<EventEntry<TopologyEvent>> events =
159 new LinkedList<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800160
Ray Milkey5df613b2014-04-15 10:50:56 -0700161 for (TopologyEvent topologyEvent : allTopologyEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700162 EventEntry<TopologyEvent> eventEntry =
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700163 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD,
164 topologyEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700165 events.add(eventEntry);
Ray Milkey269ffb92014-04-03 14:43:30 -0700166 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700167 processEvents(events);
Ray Milkey269ffb92014-04-03 14:43:30 -0700168 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800169
Ray Milkey269ffb92014-04-03 14:43:30 -0700170 /**
171 * Run the thread.
172 */
173 @Override
174 public void run() {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700175 List<EventEntry<TopologyEvent>> events =
176 new LinkedList<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800177
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 this.setName("TopologyManager.EventHandler " + this.getId());
179 startup();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800180
Ray Milkey269ffb92014-04-03 14:43:30 -0700181 //
182 // The main loop
183 //
Pavlin Radoslavov8e881a42014-06-24 16:58:07 -0700184 while (true) {
185 try {
186 EventEntry<TopologyEvent> eventEntry =
187 topologyEvents.take();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700188 events.add(eventEntry);
189 topologyEvents.drainTo(events);
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800190
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700191 processEvents(events);
192 events.clear();
Pavlin Radoslavov8e881a42014-06-24 16:58:07 -0700193 } catch (Exception exception) {
194 log.debug("Exception processing Topology Events: ",
195 exception);
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 }
198 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800199
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 /**
201 * Process all topology events.
202 *
203 * @param events the events to process.
204 */
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700205 private void processEvents(List<EventEntry<TopologyEvent>> events) {
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700206 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700207 // Process pending (new) listeners
208 //
209 processPendingListeners();
210
211 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700212 // Pre-process the events
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700213 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700214 events = eventPreprocessor.processEvents(events);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800215
Ray Milkey269ffb92014-04-03 14:43:30 -0700216 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700217 // Lock the topology while it is modified
Ray Milkey269ffb92014-04-03 14:43:30 -0700218 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700219 topology.acquireWriteLock();
Pavlin Radoslavov8ffb8bf2014-02-20 15:34:26 -0800220
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 try {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700222 // Apply the events
Ray Milkey269ffb92014-04-03 14:43:30 -0700223 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700224 // NOTE: The events are suppose to be in the proper order
225 // to naturally build and update the topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700226 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700227 for (EventEntry<TopologyEvent> event : events) {
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700228 // Ignore NO-OP events
229 if (event.isNoop()) {
230 continue;
231 }
232
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700233 TopologyEvent topologyEvent = event.eventData();
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800234
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700235 // Get the event itself
236 MastershipEvent mastershipEvent =
237 topologyEvent.getMastershipEvent();
238 SwitchEvent switchEvent = topologyEvent.getSwitchEvent();
239 PortEvent portEvent = topologyEvent.getPortEvent();
240 LinkEvent linkEvent = topologyEvent.getLinkEvent();
241 HostEvent hostEvent = topologyEvent.getHostEvent();
242 boolean wasAdded = false;
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800243
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700244 //
245 // Extract the events
246 //
247 switch (event.eventType()) {
248 case ENTRY_ADD:
249 if (mastershipEvent != null) {
250 wasAdded = addMastershipEvent(mastershipEvent);
251 }
252 if (switchEvent != null) {
253 wasAdded = addSwitch(switchEvent);
254 }
255 if (portEvent != null) {
256 wasAdded = addPort(portEvent);
257 }
258 if (linkEvent != null) {
259 wasAdded = addLink(linkEvent);
260 }
261 if (hostEvent != null) {
262 wasAdded = addHost(hostEvent);
263 }
264 // If the item wasn't added, probably it was reordered
265 if (!wasAdded) {
266 ByteBuffer id = topologyEvent.getIDasByteBuffer();
267 eventPreprocessor.reorderedEvents.put(id, topologyEvent);
268 }
269 break;
270 case ENTRY_REMOVE:
271 if (mastershipEvent != null) {
272 removeMastershipEvent(mastershipEvent);
273 }
274 if (switchEvent != null) {
275 removeSwitch(switchEvent);
276 }
277 if (portEvent != null) {
278 removePort(portEvent);
279 }
280 if (linkEvent != null) {
281 removeLink(linkEvent);
282 }
283 if (hostEvent != null) {
284 removeHost(hostEvent);
285 }
286 break;
287 default:
288 log.error("Unknown topology event {}",
289 event.eventType());
290 }
291 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700292 } finally {
293 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700294 // Topology modifications completed: Release the lock
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700296 topology.releaseWriteLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700297 }
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800298
Ray Milkey269ffb92014-04-03 14:43:30 -0700299 //
300 // Dispatch the Topology Notification Events to the applications
301 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700302 dispatchTopologyEvents();
Ray Milkey269ffb92014-04-03 14:43:30 -0700303 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800304
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 /**
306 * Receive a notification that an entry is added.
307 *
308 * @param value the value for the entry.
309 */
310 @Override
311 public void entryAdded(TopologyEvent value) {
312 EventEntry<TopologyEvent> eventEntry =
313 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD,
314 value);
315 topologyEvents.add(eventEntry);
316 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800317
Ray Milkey269ffb92014-04-03 14:43:30 -0700318 /**
319 * Receive a notification that an entry is removed.
320 *
321 * @param value the value for the entry.
322 */
323 @Override
324 public void entryRemoved(TopologyEvent value) {
325 EventEntry<TopologyEvent> eventEntry =
326 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_REMOVE,
327 value);
328 topologyEvents.add(eventEntry);
329 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800330
Ray Milkey269ffb92014-04-03 14:43:30 -0700331 /**
332 * Receive a notification that an entry is updated.
333 *
334 * @param value the value for the entry.
335 */
336 @Override
337 public void entryUpdated(TopologyEvent value) {
338 // NOTE: The ADD and UPDATE events are processed in same way
339 entryAdded(value);
340 }
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700341
342 /**
343 * Informs the event handler that a new listener has been added,
344 * and that listener expects the first event to be a snapshot of the
345 * current topology.
346 */
347 void listenerAdded() {
348 //
349 // Generate a NO-OP event so the Event Handler processing can be
350 // triggered to generate in-order a snapshot of the current
351 // topology.
352 // TODO: This is a hack.
353 //
354 EventEntry<TopologyEvent> eventEntry = EventEntry.makeNoop();
355 topologyEvents.add(eventEntry);
356 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800357 }
358
359 /**
360 * Startup processing.
361 *
362 * @param datagridService the datagrid service to use.
363 */
364 void startup(IDatagridService datagridService) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700365 eventChannel = datagridService.addListener(EVENT_CHANNEL_NAME,
366 eventHandler,
367 byte[].class,
368 TopologyEvent.class);
369 eventHandler.start();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800370 }
371
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800372 /**
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700373 * Adds a listener for topology events.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700374 *
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700375 * @param listener the listener to add.
376 * @param startFromSnapshot if true, and if the topology is not
377 * empty, the first event should be a snapshot of the current topology.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700378 */
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700379 void addListener(ITopologyListener listener, boolean startFromSnapshot) {
380 if (startFromSnapshot) {
381 newTopologyListeners.addIfAbsent(listener);
382 eventHandler.listenerAdded();
383 } else {
384 topologyListeners.addIfAbsent(listener);
385 }
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700386 }
387
388 /**
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700389 * Removes a listener for topology events. The listener will no longer
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700390 * receive topology events after this call.
391 *
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700392 * @param listener the listener to remove.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700393 */
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700394 void removeListener(ITopologyListener listener) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700395 topologyListeners.remove(listener);
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700396 newTopologyListeners.remove(listener);
397 }
398
399 /**
400 * Processes pending (new) listeners.
401 * <p>
402 * During the processing, we dispatch Topology Snapshot Events to new
403 * listeners.
404 */
405 private void processPendingListeners() {
406 if (newTopologyListeners.isEmpty()) {
407 return;
408 }
409
410 //
411 // Create the Topology Snapshot Event
412 //
413 TopologyEvents events = null;
Pavlin Radoslavov41633642014-08-11 14:24:52 -0700414 Collection<MastershipEvent> mastershipEvents =
415 lastAddMastershipEvents.values();
416 Collection<SwitchEvent> switchEvents = topology.getAllSwitchEvents();
417 Collection<PortEvent> portEvents = topology.getAllPortEvents();
418 Collection<LinkEvent> linkEvents = topology.getAllLinkEvents();
419 Collection<HostEvent> hostEvents = topology.getAllHostEvents();
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700420 if (!(mastershipEvents.isEmpty() &&
421 switchEvents.isEmpty() &&
422 portEvents.isEmpty() &&
423 linkEvents.isEmpty() &&
424 hostEvents.isEmpty())) {
425 events = new TopologyEvents(mastershipEvents,
426 switchEvents,
427 portEvents,
428 linkEvents,
429 hostEvents);
430 }
431
432 //
433 // Dispatch Snapshot Event to each new listener, and keep track
434 // of each processed listener.
435 //
436 // NOTE: We deliver the event only if it is not empty.
437 // NOTE: We need to execute the loop so we can properly
438 // move the new listeners together with the older listeners.
439 //
440 List<ITopologyListener> processedListeners = new LinkedList<>();
441 for (ITopologyListener listener : newTopologyListeners) {
442 processedListeners.add(listener);
443 // Move the new listener together with the rest of the listeners
444 topologyListeners.addIfAbsent(listener);
445
446 // Dispatch the event
447 if (events != null) {
448 listener.topologyEvents(events);
449 }
450 }
451 newTopologyListeners.removeAll(processedListeners);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700452 }
453
454 /**
Jonathan Harte37e4e22014-05-13 19:12:02 -0700455 * Dispatch Topology Events to the listeners.
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800456 */
Jonathan Harte37e4e22014-05-13 19:12:02 -0700457 private void dispatchTopologyEvents() {
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700458 if (apiAddedMastershipEvents.isEmpty() &&
459 apiRemovedMastershipEvents.isEmpty() &&
460 apiAddedSwitchEvents.isEmpty() &&
Ray Milkey269ffb92014-04-03 14:43:30 -0700461 apiRemovedSwitchEvents.isEmpty() &&
462 apiAddedPortEvents.isEmpty() &&
463 apiRemovedPortEvents.isEmpty() &&
464 apiAddedLinkEvents.isEmpty() &&
465 apiRemovedLinkEvents.isEmpty() &&
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700466 apiAddedHostEvents.isEmpty() &&
467 apiRemovedHostEvents.isEmpty()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700468 return; // No events to dispatch
469 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800470
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 if (log.isDebugEnabled()) {
472 //
473 // Debug statements
474 // TODO: Those statements should be removed in the future
475 //
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700476 for (MastershipEvent mastershipEvent : apiAddedMastershipEvents) {
477 log.debug("Dispatch Topology Event: ADDED {}",
478 mastershipEvent);
479 }
480 for (MastershipEvent mastershipEvent : apiRemovedMastershipEvents) {
481 log.debug("Dispatch Topology Event: REMOVED {}",
482 mastershipEvent);
483 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700484 for (SwitchEvent switchEvent : apiAddedSwitchEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700485 log.debug("Dispatch Topology Event: ADDED {}", switchEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700486 }
487 for (SwitchEvent switchEvent : apiRemovedSwitchEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700488 log.debug("Dispatch Topology Event: REMOVED {}", switchEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700489 }
490 for (PortEvent portEvent : apiAddedPortEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700491 log.debug("Dispatch Topology Event: ADDED {}", portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700492 }
493 for (PortEvent portEvent : apiRemovedPortEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700494 log.debug("Dispatch Topology Event: REMOVED {}", portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700495 }
496 for (LinkEvent linkEvent : apiAddedLinkEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700497 log.debug("Dispatch Topology Event: ADDED {}", linkEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700498 }
499 for (LinkEvent linkEvent : apiRemovedLinkEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700500 log.debug("Dispatch Topology Event: REMOVED {}", linkEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700501 }
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700502 for (HostEvent hostEvent : apiAddedHostEvents) {
503 log.debug("Dispatch Topology Event: ADDED {}", hostEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700504 }
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700505 for (HostEvent hostEvent : apiRemovedHostEvents) {
506 log.debug("Dispatch Topology Event: REMOVED {}", hostEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700507 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700508 }
adminbc181552014-02-21 18:36:42 -0800509
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700510 //
511 // Update the metrics
512 //
513 long totalEvents =
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700514 apiAddedMastershipEvents.size() + apiRemovedMastershipEvents.size() +
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700515 apiAddedSwitchEvents.size() + apiRemovedSwitchEvents.size() +
516 apiAddedPortEvents.size() + apiRemovedPortEvents.size() +
517 apiAddedLinkEvents.size() + apiRemovedLinkEvents.size() +
518 apiAddedHostEvents.size() + apiRemovedHostEvents.size();
519 this.listenerEventRate.mark(totalEvents);
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -0700520 this.lastEventTimestampEpochMs = System.currentTimeMillis();
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700521
522 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700523 // Allocate the events to deliver.
524 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700525 TopologyEvents events = new TopologyEvents(
Pavlin Radoslavov41633642014-08-11 14:24:52 -0700526 apiAddedMastershipEvents,
527 apiRemovedMastershipEvents,
528 apiAddedSwitchEvents,
529 apiRemovedSwitchEvents,
530 apiAddedPortEvents,
531 apiRemovedPortEvents,
532 apiAddedLinkEvents,
533 apiRemovedLinkEvents,
534 apiAddedHostEvents,
535 apiRemovedHostEvents);
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700536
537 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700538 // Deliver the events
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700539 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700540 for (ITopologyListener listener : this.topologyListeners) {
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -0700541 listener.topologyEvents(events);
Ray Milkey269ffb92014-04-03 14:43:30 -0700542 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800543
Ray Milkey269ffb92014-04-03 14:43:30 -0700544 //
545 // Cleanup
546 //
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700547 apiAddedMastershipEvents.clear();
548 apiRemovedMastershipEvents.clear();
Ray Milkey269ffb92014-04-03 14:43:30 -0700549 apiAddedSwitchEvents.clear();
550 apiRemovedSwitchEvents.clear();
551 apiAddedPortEvents.clear();
552 apiRemovedPortEvents.clear();
553 apiAddedLinkEvents.clear();
554 apiRemovedLinkEvents.clear();
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700555 apiAddedHostEvents.clear();
556 apiRemovedHostEvents.clear();
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800557 }
558
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700559 //
560 // Methods to update topology replica
561 //
562
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800563 /**
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700564 * Adds Switch Mastership event.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700565 *
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700566 * @param mastershipEvent the MastershipEvent to process.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700567 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700568 */
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700569 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700570 private boolean addMastershipEvent(MastershipEvent mastershipEvent) {
571 log.debug("Added Mastership event {}", mastershipEvent);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700572 lastAddMastershipEvents.put(mastershipEvent.getIDasByteBuffer(),
573 mastershipEvent);
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700574 apiAddedMastershipEvents.add(mastershipEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700575 return true;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700576 }
577
578 /**
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700579 * Removes Switch Mastership event.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700580 *
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700581 * @param mastershipEvent the MastershipEvent to process.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700582 */
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700583 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700584 private void removeMastershipEvent(MastershipEvent mastershipEvent) {
585 log.debug("Removed Mastership event {}", mastershipEvent);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700586 lastAddMastershipEvents.remove(mastershipEvent.getIDasByteBuffer());
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700587 apiRemovedMastershipEvents.add(mastershipEvent);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700588 }
589
590 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700591 * Adds a switch to the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800592 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700593 * @param switchEvent the SwitchEvent with the switch to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700594 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800595 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700596 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700597 private boolean addSwitch(SwitchEvent switchEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700598 if (log.isDebugEnabled()) {
599 SwitchEvent sw = topology.getSwitchEvent(switchEvent.getDpid());
600 if (sw != null) {
601 log.debug("Update {}", switchEvent);
602 } else {
603 log.debug("Added {}", switchEvent);
604 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700605 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700606 topology.putSwitch(switchEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700607 apiAddedSwitchEvents.add(switchEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700608 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800609 }
610
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800611 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700612 * Removes a switch from the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700613 * <p/>
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700614 * It will call {@link #removePort(PortEvent)} for each ports on this
615 * switch.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800616 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700617 * @param switchEvent the SwitchEvent with the switch to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800618 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700619 @GuardedBy("topology.writeLock")
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800620 private void removeSwitch(SwitchEvent switchEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700621 final Dpid dpid = switchEvent.getDpid();
622
623 SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
624 if (swInTopo == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700625 log.warn("Switch {} already removed, ignoring", switchEvent);
626 return;
627 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800628
Ray Milkey269ffb92014-04-03 14:43:30 -0700629 //
630 // Remove all Ports on the Switch
631 //
632 ArrayList<PortEvent> portsToRemove = new ArrayList<>();
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700633 for (Port port : topology.getPorts(dpid)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700634 log.warn("Port {} on Switch {} should be removed prior to removing Switch. Removing Port now.",
635 port, switchEvent);
Yuta HIGUCHIcd14dda2014-07-24 09:57:22 -0700636 PortEvent portEvent = new PortEvent(port.getSwitchPort());
Ray Milkey269ffb92014-04-03 14:43:30 -0700637 portsToRemove.add(portEvent);
638 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700639 for (PortEvent portEvent : portsToRemove) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700640 removePort(portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700641 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800642
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700643 log.debug("Removed {}", swInTopo);
644 topology.removeSwitch(dpid);
645 apiRemovedSwitchEvents.add(swInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800646 }
647
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800648 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700649 * Adds a port to the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800650 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700651 * @param portEvent the PortEvent with the port to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700652 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800653 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700654 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700655 private boolean addPort(PortEvent portEvent) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700656 Switch sw = topology.getSwitch(portEvent.getDpid());
Ray Milkey269ffb92014-04-03 14:43:30 -0700657 if (sw == null) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700658 // Reordered event
Jonathan Hartf1675202014-05-23 14:59:07 -0700659 log.debug("{} reordered because switch is null", portEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700660 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700661 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800662
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700663 if (log.isDebugEnabled()) {
664 PortEvent port = topology.getPortEvent(portEvent.getSwitchPort());
665 if (port != null) {
666 log.debug("Update {}", portEvent);
667 } else {
668 log.debug("Added {}", portEvent);
669 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700670 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700671 topology.putPort(portEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700672 apiAddedPortEvents.add(portEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700673 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800674 }
675
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800676 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700677 * Removes a port from the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700678 * <p/>
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700679 * It will remove attachment points from each hosts on this port
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700680 * and call {@link #removeLink(LinkEvent)} for each links on this port.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800681 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700682 * @param portEvent the PortEvent with the port to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800683 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700684 @GuardedBy("topology.writeLock")
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800685 private void removePort(PortEvent portEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700686 SwitchEvent sw = topology.getSwitchEvent(portEvent.getDpid());
Ray Milkey269ffb92014-04-03 14:43:30 -0700687 if (sw == null) {
688 log.warn("Parent Switch for Port {} already removed, ignoring",
689 portEvent);
690 return;
691 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800692
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700693 final SwitchPort switchPort = portEvent.getSwitchPort();
694 PortEvent portInTopo = topology.getPortEvent(switchPort);
695 if (portInTopo == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700696 log.warn("Port {} already removed, ignoring", portEvent);
697 return;
698 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800699
Ray Milkey269ffb92014-04-03 14:43:30 -0700700 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700701 // Remove all Host attachment points bound to this Port
Ray Milkey269ffb92014-04-03 14:43:30 -0700702 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700703 List<HostEvent> hostsToUpdate = new ArrayList<>();
704 for (Host host : topology.getHosts(switchPort)) {
705 log.debug("Removing Host {} on Port {}", host, portInTopo);
706 HostEvent hostEvent = topology.getHostEvent(host.getMacAddress());
707 hostsToUpdate.add(hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700708 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700709 for (HostEvent hostEvent : hostsToUpdate) {
710 HostEvent newHostEvent = new HostEvent(hostEvent);
711 newHostEvent.removeAttachmentPoint(switchPort);
712 newHostEvent.freeze();
713
714 // TODO should this event be fired inside #addHost?
715 if (newHostEvent.getAttachmentPoints().isEmpty()) {
716 // No more attachment point left -> remove Host
717 removeHost(hostEvent);
718 } else {
719 // Update Host
720 addHost(newHostEvent);
721 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700722 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800723
Ray Milkey269ffb92014-04-03 14:43:30 -0700724 //
725 // Remove all Links connected to the Port
726 //
727 Set<Link> links = new HashSet<>();
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700728 links.addAll(topology.getOutgoingLinks(switchPort));
729 links.addAll(topology.getIncomingLinks(switchPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700730 for (Link link : links) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700731 if (link == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700732 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700733 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700734 LinkEvent linkEvent = topology.getLinkEvent(link.getLinkTuple());
735 if (linkEvent != null) {
736 log.debug("Removing Link {} on Port {}", link, portInTopo);
737 removeLink(linkEvent);
738 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700739 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800740
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700741 // Remove the Port from Topology
742 log.debug("Removed {}", portInTopo);
743 topology.removePort(switchPort);
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800744
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700745 apiRemovedPortEvents.add(portInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800746 }
747
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800748 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700749 * Adds a link to the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700750 * <p/>
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700751 * It will remove attachment points from each hosts using the same ports.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800752 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700753 * @param linkEvent the LinkEvent with the link to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700754 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800755 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700756 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700757 private boolean addLink(LinkEvent linkEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700758 PortEvent srcPort = topology.getPortEvent(linkEvent.getSrc());
759 PortEvent dstPort = topology.getPortEvent(linkEvent.getDst());
Ray Milkey269ffb92014-04-03 14:43:30 -0700760 if ((srcPort == null) || (dstPort == null)) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700761 // Reordered event
Jonathan Hartf1675202014-05-23 14:59:07 -0700762 log.debug("{} reordered because {} port is null", linkEvent,
763 (srcPort == null) ? "src" : "dst");
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700764 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700765 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800766
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700767 //
768 // XXX domain knowledge: Sanity check: Port cannot have both Link and
769 // Host.
770 //
771 // FIXME: Potentially local replica may not be up-to-date yet due to
772 // Hazelcast delay.
773 // FIXME: May need to manage local truth and use them instead.
774 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700775 if (topology.getLinkEvent(linkEvent.getLinkTuple()) == null) {
776 // Only check for existing Host when adding new Link.
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700777 // Remove all Hosts attached to the ports on both ends
778
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700779 Set<HostEvent> hostsToUpdate =
780 new TreeSet<>(new Comparator<HostEvent>() {
781 // Comparison only using ID(=MAC)
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700782 @Override
783 public int compare(HostEvent o1, HostEvent o2) {
784 return Long.compare(o1.getMac().toLong(), o2.getMac().toLong());
785 }
786 });
787
788 List<SwitchPort> portsToCheck = Arrays.asList(
789 srcPort.getSwitchPort(),
790 dstPort.getSwitchPort());
791
792 // Enumerate Host which needs to be updated by this Link add event
793 for (SwitchPort port : portsToCheck) {
794 for (Host host : topology.getHosts(port)) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700795 log.error("Host {} on Port {} should have been removed prior to adding Link {}",
796 host, port, linkEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700797
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700798 HostEvent hostEvent =
799 topology.getHostEvent(host.getMacAddress());
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700800 hostsToUpdate.add(hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700801 }
802 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700803 // Remove attachment point from them
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700804 for (HostEvent hostEvent : hostsToUpdate) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700805 // Remove port from attachment point and update
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700806 HostEvent newHostEvent = new HostEvent(hostEvent);
807 newHostEvent.removeAttachmentPoint(srcPort.getSwitchPort());
808 newHostEvent.removeAttachmentPoint(dstPort.getSwitchPort());
809 newHostEvent.freeze();
810
811 // TODO should this event be fired inside #addHost?
812 if (newHostEvent.getAttachmentPoints().isEmpty()) {
813 // No more attachment point left -> remove Host
814 removeHost(hostEvent);
815 } else {
816 // Update Host
817 addHost(newHostEvent);
818 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700819 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700820 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800821
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700822 if (log.isDebugEnabled()) {
823 LinkEvent link = topology.getLinkEvent(linkEvent.getLinkTuple());
824 if (link != null) {
825 log.debug("Update {}", linkEvent);
826 } else {
827 log.debug("Added {}", linkEvent);
828 }
829 }
830 topology.putLink(linkEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700831 apiAddedLinkEvents.add(linkEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700832 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800833 }
834
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800835 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700836 * Removes a link from the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800837 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700838 * @param linkEvent the LinkEvent with the link to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800839 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700840 @GuardedBy("topology.writeLock")
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800841 private void removeLink(LinkEvent linkEvent) {
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700842 Port srcPort = topology.getPort(linkEvent.getSrc().getDpid(),
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700843 linkEvent.getSrc().getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700844 if (srcPort == null) {
845 log.warn("Src Port for Link {} already removed, ignoring",
846 linkEvent);
847 return;
848 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800849
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700850 Port dstPort = topology.getPort(linkEvent.getDst().getDpid(),
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700851 linkEvent.getDst().getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700852 if (dstPort == null) {
853 log.warn("Dst Port for Link {} already removed, ignoring",
854 linkEvent);
855 return;
856 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800857
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700858 LinkEvent linkInTopo = topology.getLinkEvent(linkEvent.getLinkTuple(),
859 linkEvent.getType());
860 if (linkInTopo == null) {
861 log.warn("Link {} already removed, ignoring", linkEvent);
862 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700863 }
Jonathan Hart25bd53e2014-04-30 23:44:09 -0700864
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700865 if (log.isDebugEnabled()) {
866 // only do sanity check on debug level
867
868 Link linkIn = dstPort.getIncomingLink(linkEvent.getType());
869 if (linkIn == null) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700870 log.warn("Link {} already removed on destination Port",
871 linkEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700872 }
873 Link linkOut = srcPort.getOutgoingLink(linkEvent.getType());
874 if (linkOut == null) {
875 log.warn("Link {} already removed on src Port", linkEvent);
876 }
Jonathan Hart25bd53e2014-04-30 23:44:09 -0700877 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800878
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700879 log.debug("Removed {}", linkInTopo);
880 topology.removeLink(linkEvent.getLinkTuple(), linkEvent.getType());
881 apiRemovedLinkEvents.add(linkInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800882 }
883
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800884 /**
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700885 * Adds a host to the topology replica.
Ray Milkey269ffb92014-04-03 14:43:30 -0700886 * <p/>
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700887 * TODO: Host-related work is incomplete.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800888 * TODO: Eventually, we might need to consider reordering
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700889 * or {@link #addLink(LinkEvent)} and {@link #addHost(HostEvent)} events
890 * on the same port.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800891 *
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700892 * @param hostEvent the HostEvent with the host to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700893 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800894 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700895 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700896 private boolean addHost(HostEvent hostEvent) {
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800897
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700898 // TODO Decide how to handle update scenario.
899 // If the new HostEvent has less attachment point compared to
900 // existing HostEvent, what should the event be?
901 // - AddHostEvent with some attachment point removed? (current behavior)
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800902
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700903 // create unfrozen copy
904 // for removing attachment points which already has a link
905 HostEvent modifiedHostEvent = new HostEvent(hostEvent);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800906
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700907 // Verify each attachment point
Ray Milkey269ffb92014-04-03 14:43:30 -0700908 boolean attachmentFound = false;
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700909 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
910 // XXX domain knowledge: Port must exist before Host
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700911 // but this knowledge cannot be pushed down to driver.
912
Ray Milkey269ffb92014-04-03 14:43:30 -0700913 // Attached Ports must exist
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700914 Port port = topology.getPort(swp.getDpid(), swp.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700915 if (port == null) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700916 log.debug("{} reordered because port {} was not there",
917 hostEvent, swp);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700918 // Reordered event
919 return false; // should not continue if re-applying later
Ray Milkey269ffb92014-04-03 14:43:30 -0700920 }
921 // Attached Ports must not have Link
922 if (port.getOutgoingLink() != null ||
923 port.getIncomingLink() != null) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700924 log.warn("Link (Out:{},In:{}) exist on the attachment point. "
925 + "Ignoring this attachmentpoint ({}) from {}.",
926 port.getOutgoingLink(), port.getIncomingLink(),
927 swp, modifiedHostEvent);
928 // FIXME Should either reject, reorder this HostEvent,
929 // or remove attachment point from given HostEvent
930 // Removing attachment point from given HostEvent for now.
931 modifiedHostEvent.removeAttachmentPoint(swp);
Ray Milkey269ffb92014-04-03 14:43:30 -0700932 continue;
933 }
934
Ray Milkey269ffb92014-04-03 14:43:30 -0700935 attachmentFound = true;
936 }
937
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700938 // Update the host in the topology
Ray Milkey269ffb92014-04-03 14:43:30 -0700939 if (attachmentFound) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700940 if (modifiedHostEvent.getAttachmentPoints().isEmpty()) {
941 log.warn("No valid attachment point left. Ignoring."
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700942 + "original: {}, modified: {}",
943 hostEvent, modifiedHostEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700944 // TODO Should we call #removeHost to trigger remove event?
945 // only if this call is update.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700946 return false;
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700947 }
948
949 if (log.isDebugEnabled()) {
950 HostEvent host = topology.getHostEvent(hostEvent.getMac());
951 if (host != null) {
952 log.debug("Update {}", modifiedHostEvent);
953 } else {
954 log.debug("Added {}", modifiedHostEvent);
955 }
956 }
957 topology.putHost(modifiedHostEvent.freeze());
958 apiAddedHostEvents.add(modifiedHostEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700959 return true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700960 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700961 return false;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800962 }
963
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800964 /**
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700965 * Removes a host from the topology replica.
Ray Milkey269ffb92014-04-03 14:43:30 -0700966 * <p/>
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700967 * TODO: Host-related work is incomplete.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800968 *
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700969 * @param hostEvent the Host Event with the host to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800970 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700971 @GuardedBy("topology.writeLock")
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700972 private void removeHost(HostEvent hostEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700973
974 final MACAddress mac = hostEvent.getMac();
975 HostEvent hostInTopo = topology.getHostEvent(mac);
976 if (hostInTopo == null) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700977 log.warn("Host {} already removed, ignoring", hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700978 return;
979 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800980
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700981 log.debug("Removed {}", hostInTopo);
982 topology.removeHost(mac);
983 apiRemovedHostEvents.add(hostInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800984 }
Jonathan Hart062a2e82014-02-03 09:41:57 -0800985}