blob: daa334b625e17fb773b2f6c569a2935bcd62f382 [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;
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -070032import net.onrc.onos.core.util.serializers.KryoFactory;
Yuta HIGUCHI5c8cbeb2014-06-27 11:13:48 -070033
Jonathan Hart062a2e82014-02-03 09:41:57 -080034import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070037import com.codahale.metrics.Gauge;
38import com.codahale.metrics.Meter;
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -070039import com.esotericsoftware.kryo.Kryo;
40
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080041/**
Jonathan Harte37e4e22014-05-13 19:12:02 -070042 * The TopologyManager receives topology updates from the southbound discovery
43 * modules and from other ONOS instances. These updates are processed and
44 * applied to the in-memory topology instance.
Ray Milkey269ffb92014-04-03 14:43:30 -070045 * <p/>
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -080046 * - Maintain Invariant/Relationships between Topology Objects.
Ray Milkey269ffb92014-04-03 14:43:30 -070047 * <p/>
Yuta HIGUCHI765cd0d2014-02-06 12:46:41 -080048 * TODO To be synchronized based on TopologyEvent Notification.
Ray Milkey269ffb92014-04-03 14:43:30 -070049 * <p/>
Yuta HIGUCHIcb951982014-02-11 13:31:44 -080050 * TODO TBD: Caller is expected to maintain parent/child calling order. Parent
Yuta HIGUCHI1c700102014-02-12 16:30:52 -080051 * Object must exist before adding sub component(Add Switch -> Port).
Ray Milkey269ffb92014-04-03 14:43:30 -070052 * <p/>
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -080053 * TODO TBD: This class may delay the requested change to handle event
54 * re-ordering. e.g.) Link Add came in, but Switch was not there.
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080055 */
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070056public class TopologyManager {
Jonathan Hart062a2e82014-02-03 09:41:57 -080057
Yuta HIGUCHI80829d12014-02-05 20:16:56 -080058 private static final Logger log = LoggerFactory
Ray Milkey269ffb92014-04-03 14:43:30 -070059 .getLogger(TopologyManager.class);
Yuta HIGUCHIcd922f42014-02-11 18:59:11 -080060
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080061 private IEventChannel<byte[], TopologyEvent> eventChannel;
Jonathan Hart10a7e2b2014-02-21 18:30:08 -080062 public static final String EVENT_CHANNEL_NAME = "onos.topology";
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -080063 private EventHandler eventHandler = new EventHandler();
64
Jonathan Harte37e4e22014-05-13 19:12:02 -070065 private final TopologyImpl topology = new TopologyImpl();
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -070066 private Kryo kryo = KryoFactory.newKryoObject();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -070067 private TopologyEventPreprocessor eventPreprocessor;
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -070068 private CopyOnWriteArrayList<ITopologyListener> topologyListeners =
69 new CopyOnWriteArrayList<>();
Pavlin Radoslavov054cd592014-08-07 20:57:16 -070070 private CopyOnWriteArrayList<ITopologyListener> newTopologyListeners =
71 new CopyOnWriteArrayList<>();
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -080072
Pavlin Radoslavov706add22014-02-20 12:15:59 -080073 //
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070074 // Metrics
75 //
76 private static final MetricsComponent METRICS_COMPONENT =
77 OnosMetrics.registerComponent("Topology");
78 private static final MetricsFeature METRICS_FEATURE_EVENT_NOTIFICATION =
79 METRICS_COMPONENT.registerFeature("EventNotification");
80 //
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070081 // Timestamp of the last Topology event (ms from the Epoch)
82 private volatile long lastEventTimestampEpochMs = 0;
83 private final Gauge<Long> gaugeLastEventTimestampEpochMs =
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070084 OnosMetrics.registerMetric(METRICS_COMPONENT,
85 METRICS_FEATURE_EVENT_NOTIFICATION,
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070086 "LastEventTimestamp.EpochMs",
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070087 new Gauge<Long>() {
88 @Override
89 public Long getValue() {
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -070090 return lastEventTimestampEpochMs;
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -070091 }
92 });
93 // Rate of the Topology events published to the Topology listeners
94 private final Meter listenerEventRate =
95 OnosMetrics.createMeter(METRICS_COMPONENT,
96 METRICS_FEATURE_EVENT_NOTIFICATION,
97 "ListenerEventRate");
98
99 //
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700100 // Local state for keeping the last ADD Mastership Event entries.
101 // TODO: In the future, we might have to keep this state somewhere else.
102 //
103 private Map<ByteBuffer, MastershipEvent> lastAddMastershipEvents =
104 new HashMap<>();
105
106 //
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800107 // Local state for keeping track of the application event notifications
108 //
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700109 // - Queue of events, which will be dispatched to local listeners
110 // on next notification.
Yuta HIGUCHI703696c2014-06-25 20:36:45 -0700111
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700112 private List<MastershipEvent> apiAddedMastershipEvents =
113 new LinkedList<>();
114 private List<MastershipEvent> apiRemovedMastershipEvents =
115 new LinkedList<>();
Yuta HIGUCHI703696c2014-06-25 20:36:45 -0700116 private List<SwitchEvent> apiAddedSwitchEvents = new LinkedList<>();
117 private List<SwitchEvent> apiRemovedSwitchEvents = new LinkedList<>();
118 private List<PortEvent> apiAddedPortEvents = new LinkedList<>();
119 private List<PortEvent> apiRemovedPortEvents = new LinkedList<>();
120 private List<LinkEvent> apiAddedLinkEvents = new LinkedList<>();
121 private List<LinkEvent> apiRemovedLinkEvents = new LinkedList<>();
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700122 private List<HostEvent> apiAddedHostEvents = new LinkedList<>();
123 private List<HostEvent> apiRemovedHostEvents = new LinkedList<>();
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800124
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800125 /**
126 * Constructor.
127 *
Jonathan Harte37e4e22014-05-13 19:12:02 -0700128 * @param registryService the Registry Service to use.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800129 */
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700130 public TopologyManager(IControllerRegistryService registryService) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700131 this.eventPreprocessor =
132 new TopologyEventPreprocessor(registryService);
Yuta HIGUCHI80829d12014-02-05 20:16:56 -0800133 }
Yuta HIGUCHI181d34d2014-02-05 15:05:46 -0800134
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800135 /**
Jonathan Harte37e4e22014-05-13 19:12:02 -0700136 * Get the Topology.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800137 *
Jonathan Harte37e4e22014-05-13 19:12:02 -0700138 * @return the Topology.
Pavlin Radoslavovc1cfde52014-02-19 11:35:29 -0800139 */
Jonathan Harte37e4e22014-05-13 19:12:02 -0700140 Topology getTopology() {
141 return topology;
Pavlin Radoslavov6d224ee2014-02-18 16:43:15 -0800142 }
143
Yuta HIGUCHI4bfdd532014-02-07 13:47:36 -0800144 /**
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800145 * Event handler class.
146 */
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700147 class EventHandler extends Thread implements
Ray Milkey269ffb92014-04-03 14:43:30 -0700148 IEventChannelListener<byte[], TopologyEvent> {
149 private BlockingQueue<EventEntry<TopologyEvent>> topologyEvents =
150 new LinkedBlockingQueue<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800151
Ray Milkey269ffb92014-04-03 14:43:30 -0700152 /**
153 * Startup processing.
154 */
155 private void startup() {
156 //
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700157 // Read all topology state
Ray Milkey269ffb92014-04-03 14:43:30 -0700158 //
Ray Milkey5df613b2014-04-15 10:50:56 -0700159 Collection<TopologyEvent> allTopologyEvents =
Ray Milkey269ffb92014-04-03 14:43:30 -0700160 eventChannel.getAllEntries();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700161 List<EventEntry<TopologyEvent>> events =
162 new LinkedList<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800163
Ray Milkey5df613b2014-04-15 10:50:56 -0700164 for (TopologyEvent topologyEvent : allTopologyEvents) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700165 EventEntry<TopologyEvent> eventEntry =
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700166 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD,
167 topologyEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700168 events.add(eventEntry);
Ray Milkey269ffb92014-04-03 14:43:30 -0700169 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700170 processEvents(events);
Ray Milkey269ffb92014-04-03 14:43:30 -0700171 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800172
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 /**
174 * Run the thread.
175 */
176 @Override
177 public void run() {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700178 List<EventEntry<TopologyEvent>> events =
179 new LinkedList<EventEntry<TopologyEvent>>();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800180
Ray Milkey269ffb92014-04-03 14:43:30 -0700181 this.setName("TopologyManager.EventHandler " + this.getId());
182 startup();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800183
Ray Milkey269ffb92014-04-03 14:43:30 -0700184 //
185 // The main loop
186 //
Pavlin Radoslavov8e881a42014-06-24 16:58:07 -0700187 while (true) {
188 try {
189 EventEntry<TopologyEvent> eventEntry =
190 topologyEvents.take();
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700191 events.add(eventEntry);
192 topologyEvents.drainTo(events);
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800193
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700194 processEvents(events);
195 events.clear();
Pavlin Radoslavov8e881a42014-06-24 16:58:07 -0700196 } catch (Exception exception) {
197 log.debug("Exception processing Topology Events: ",
198 exception);
Ray Milkey269ffb92014-04-03 14:43:30 -0700199 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 }
201 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800202
Ray Milkey269ffb92014-04-03 14:43:30 -0700203 /**
204 * Process all topology events.
205 *
206 * @param events the events to process.
207 */
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700208 private void processEvents(List<EventEntry<TopologyEvent>> events) {
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700209 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700210 // Process pending (new) listeners
211 //
212 processPendingListeners();
213
214 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700215 // Pre-process the events
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700216 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700217 events = eventPreprocessor.processEvents(events);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800218
Ray Milkey269ffb92014-04-03 14:43:30 -0700219 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700220 // Lock the topology while it is modified
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700222 topology.acquireWriteLock();
Pavlin Radoslavov8ffb8bf2014-02-20 15:34:26 -0800223
Ray Milkey269ffb92014-04-03 14:43:30 -0700224 try {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700225 // Apply the events
Ray Milkey269ffb92014-04-03 14:43:30 -0700226 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700227 // NOTE: The events are suppose to be in the proper order
228 // to naturally build and update the topology.
Ray Milkey269ffb92014-04-03 14:43:30 -0700229 //
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700230 for (EventEntry<TopologyEvent> event : events) {
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700231 // Ignore NO-OP events
232 if (event.isNoop()) {
233 continue;
234 }
235
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700236 TopologyEvent topologyEvent = event.eventData();
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800237
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700238 // Get the event itself
239 MastershipEvent mastershipEvent =
240 topologyEvent.getMastershipEvent();
241 SwitchEvent switchEvent = topologyEvent.getSwitchEvent();
242 PortEvent portEvent = topologyEvent.getPortEvent();
243 LinkEvent linkEvent = topologyEvent.getLinkEvent();
244 HostEvent hostEvent = topologyEvent.getHostEvent();
245 boolean wasAdded = false;
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800246
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700247 //
248 // Extract the events
249 //
250 switch (event.eventType()) {
251 case ENTRY_ADD:
252 if (mastershipEvent != null) {
253 wasAdded = addMastershipEvent(mastershipEvent);
254 }
255 if (switchEvent != null) {
256 wasAdded = addSwitch(switchEvent);
257 }
258 if (portEvent != null) {
259 wasAdded = addPort(portEvent);
260 }
261 if (linkEvent != null) {
262 wasAdded = addLink(linkEvent);
263 }
264 if (hostEvent != null) {
265 wasAdded = addHost(hostEvent);
266 }
267 // If the item wasn't added, probably it was reordered
268 if (!wasAdded) {
269 ByteBuffer id = topologyEvent.getIDasByteBuffer();
270 eventPreprocessor.reorderedEvents.put(id, topologyEvent);
271 }
272 break;
273 case ENTRY_REMOVE:
274 if (mastershipEvent != null) {
275 removeMastershipEvent(mastershipEvent);
276 }
277 if (switchEvent != null) {
278 removeSwitch(switchEvent);
279 }
280 if (portEvent != null) {
281 removePort(portEvent);
282 }
283 if (linkEvent != null) {
284 removeLink(linkEvent);
285 }
286 if (hostEvent != null) {
287 removeHost(hostEvent);
288 }
289 break;
290 default:
291 log.error("Unknown topology event {}",
292 event.eventType());
293 }
294 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 } finally {
296 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700297 // Topology modifications completed: Release the lock
Ray Milkey269ffb92014-04-03 14:43:30 -0700298 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700299 topology.releaseWriteLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700300 }
Yuta HIGUCHI3aca81a2014-02-23 12:41:19 -0800301
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 //
303 // Dispatch the Topology Notification Events to the applications
304 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700305 dispatchTopologyEvents();
Ray Milkey269ffb92014-04-03 14:43:30 -0700306 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800307
Ray Milkey269ffb92014-04-03 14:43:30 -0700308 /**
309 * Receive a notification that an entry is added.
310 *
311 * @param value the value for the entry.
312 */
313 @Override
314 public void entryAdded(TopologyEvent value) {
315 EventEntry<TopologyEvent> eventEntry =
316 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_ADD,
317 value);
318 topologyEvents.add(eventEntry);
319 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800320
Ray Milkey269ffb92014-04-03 14:43:30 -0700321 /**
322 * Receive a notification that an entry is removed.
323 *
324 * @param value the value for the entry.
325 */
326 @Override
327 public void entryRemoved(TopologyEvent value) {
328 EventEntry<TopologyEvent> eventEntry =
329 new EventEntry<TopologyEvent>(EventEntry.Type.ENTRY_REMOVE,
330 value);
331 topologyEvents.add(eventEntry);
332 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800333
Ray Milkey269ffb92014-04-03 14:43:30 -0700334 /**
335 * Receive a notification that an entry is updated.
336 *
337 * @param value the value for the entry.
338 */
339 @Override
340 public void entryUpdated(TopologyEvent value) {
341 // NOTE: The ADD and UPDATE events are processed in same way
342 entryAdded(value);
343 }
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700344
345 /**
346 * Informs the event handler that a new listener has been added,
347 * and that listener expects the first event to be a snapshot of the
348 * current topology.
349 */
350 void listenerAdded() {
351 //
352 // Generate a NO-OP event so the Event Handler processing can be
353 // triggered to generate in-order a snapshot of the current
354 // topology.
355 // TODO: This is a hack.
356 //
357 EventEntry<TopologyEvent> eventEntry = EventEntry.makeNoop();
358 topologyEvents.add(eventEntry);
359 }
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800360 }
361
362 /**
363 * Startup processing.
364 *
365 * @param datagridService the datagrid service to use.
366 */
367 void startup(IDatagridService datagridService) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700368 eventChannel = datagridService.addListener(EVENT_CHANNEL_NAME,
369 eventHandler,
370 byte[].class,
371 TopologyEvent.class);
372 eventHandler.start();
Pavlin Radoslavov721a2e02014-02-14 23:40:14 -0800373 }
374
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800375 /**
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700376 * Adds a listener for topology events.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700377 *
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700378 * @param listener the listener to add.
379 * @param startFromSnapshot if true, and if the topology is not
380 * empty, the first event should be a snapshot of the current topology.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700381 */
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700382 void addListener(ITopologyListener listener, boolean startFromSnapshot) {
383 if (startFromSnapshot) {
384 newTopologyListeners.addIfAbsent(listener);
385 eventHandler.listenerAdded();
386 } else {
387 topologyListeners.addIfAbsent(listener);
388 }
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700389 }
390
391 /**
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700392 * Removes a listener for topology events. The listener will no longer
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700393 * receive topology events after this call.
394 *
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700395 * @param listener the listener to remove.
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700396 */
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700397 void removeListener(ITopologyListener listener) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700398 topologyListeners.remove(listener);
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700399 newTopologyListeners.remove(listener);
400 }
401
402 /**
403 * Processes pending (new) listeners.
404 * <p>
405 * During the processing, we dispatch Topology Snapshot Events to new
406 * listeners.
407 */
408 private void processPendingListeners() {
409 if (newTopologyListeners.isEmpty()) {
410 return;
411 }
412
413 //
414 // Create the Topology Snapshot Event
415 //
416 TopologyEvents events = null;
Pavlin Radoslavov41633642014-08-11 14:24:52 -0700417 Collection<MastershipEvent> mastershipEvents =
418 lastAddMastershipEvents.values();
419 Collection<SwitchEvent> switchEvents = topology.getAllSwitchEvents();
420 Collection<PortEvent> portEvents = topology.getAllPortEvents();
421 Collection<LinkEvent> linkEvents = topology.getAllLinkEvents();
422 Collection<HostEvent> hostEvents = topology.getAllHostEvents();
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700423 if (!(mastershipEvents.isEmpty() &&
424 switchEvents.isEmpty() &&
425 portEvents.isEmpty() &&
426 linkEvents.isEmpty() &&
427 hostEvents.isEmpty())) {
428 events = new TopologyEvents(mastershipEvents,
429 switchEvents,
430 portEvents,
431 linkEvents,
432 hostEvents);
433 }
434
435 //
436 // Dispatch Snapshot Event to each new listener, and keep track
437 // of each processed listener.
438 //
439 // NOTE: We deliver the event only if it is not empty.
440 // NOTE: We need to execute the loop so we can properly
441 // move the new listeners together with the older listeners.
442 //
443 List<ITopologyListener> processedListeners = new LinkedList<>();
444 for (ITopologyListener listener : newTopologyListeners) {
445 processedListeners.add(listener);
446 // Move the new listener together with the rest of the listeners
447 topologyListeners.addIfAbsent(listener);
448
449 // Dispatch the event
450 if (events != null) {
451 listener.topologyEvents(events);
452 }
453 }
454 newTopologyListeners.removeAll(processedListeners);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700455 }
456
457 /**
Jonathan Harte37e4e22014-05-13 19:12:02 -0700458 * Dispatch Topology Events to the listeners.
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800459 */
Jonathan Harte37e4e22014-05-13 19:12:02 -0700460 private void dispatchTopologyEvents() {
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700461 if (apiAddedMastershipEvents.isEmpty() &&
462 apiRemovedMastershipEvents.isEmpty() &&
463 apiAddedSwitchEvents.isEmpty() &&
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 apiRemovedSwitchEvents.isEmpty() &&
465 apiAddedPortEvents.isEmpty() &&
466 apiRemovedPortEvents.isEmpty() &&
467 apiAddedLinkEvents.isEmpty() &&
468 apiRemovedLinkEvents.isEmpty() &&
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700469 apiAddedHostEvents.isEmpty() &&
470 apiRemovedHostEvents.isEmpty()) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 return; // No events to dispatch
472 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800473
Ray Milkey269ffb92014-04-03 14:43:30 -0700474 if (log.isDebugEnabled()) {
475 //
476 // Debug statements
477 // TODO: Those statements should be removed in the future
478 //
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700479 for (MastershipEvent mastershipEvent : apiAddedMastershipEvents) {
480 log.debug("Dispatch Topology Event: ADDED {}",
481 mastershipEvent);
482 }
483 for (MastershipEvent mastershipEvent : apiRemovedMastershipEvents) {
484 log.debug("Dispatch Topology Event: REMOVED {}",
485 mastershipEvent);
486 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700487 for (SwitchEvent switchEvent : apiAddedSwitchEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700488 log.debug("Dispatch Topology Event: ADDED {}", switchEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700489 }
490 for (SwitchEvent switchEvent : apiRemovedSwitchEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700491 log.debug("Dispatch Topology Event: REMOVED {}", switchEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700492 }
493 for (PortEvent portEvent : apiAddedPortEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700494 log.debug("Dispatch Topology Event: ADDED {}", portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700495 }
496 for (PortEvent portEvent : apiRemovedPortEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700497 log.debug("Dispatch Topology Event: REMOVED {}", portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700498 }
499 for (LinkEvent linkEvent : apiAddedLinkEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700500 log.debug("Dispatch Topology Event: ADDED {}", linkEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700501 }
502 for (LinkEvent linkEvent : apiRemovedLinkEvents) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700503 log.debug("Dispatch Topology Event: REMOVED {}", linkEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700504 }
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700505 for (HostEvent hostEvent : apiAddedHostEvents) {
506 log.debug("Dispatch Topology Event: ADDED {}", hostEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700507 }
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700508 for (HostEvent hostEvent : apiRemovedHostEvents) {
509 log.debug("Dispatch Topology Event: REMOVED {}", hostEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700510 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700511 }
adminbc181552014-02-21 18:36:42 -0800512
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700513 //
514 // Update the metrics
515 //
516 long totalEvents =
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700517 apiAddedMastershipEvents.size() + apiRemovedMastershipEvents.size() +
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700518 apiAddedSwitchEvents.size() + apiRemovedSwitchEvents.size() +
519 apiAddedPortEvents.size() + apiRemovedPortEvents.size() +
520 apiAddedLinkEvents.size() + apiRemovedLinkEvents.size() +
521 apiAddedHostEvents.size() + apiRemovedHostEvents.size();
522 this.listenerEventRate.mark(totalEvents);
Pavlin Radoslavovc49917c2014-07-23 12:16:29 -0700523 this.lastEventTimestampEpochMs = System.currentTimeMillis();
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700524
525 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700526 // Allocate the events to deliver.
527 //
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700528 TopologyEvents events = new TopologyEvents(
Pavlin Radoslavov41633642014-08-11 14:24:52 -0700529 apiAddedMastershipEvents,
530 apiRemovedMastershipEvents,
531 apiAddedSwitchEvents,
532 apiRemovedSwitchEvents,
533 apiAddedPortEvents,
534 apiRemovedPortEvents,
535 apiAddedLinkEvents,
536 apiRemovedLinkEvents,
537 apiAddedHostEvents,
538 apiRemovedHostEvents);
Pavlin Radoslavov054cd592014-08-07 20:57:16 -0700539
540 //
Ray Milkey269ffb92014-04-03 14:43:30 -0700541 // Deliver the events
Pavlin Radoslavovd4f40372014-07-18 16:58:40 -0700542 //
Jonathan Harte37e4e22014-05-13 19:12:02 -0700543 for (ITopologyListener listener : this.topologyListeners) {
Pavlin Radoslavov4eaab992014-07-03 18:39:42 -0700544 listener.topologyEvents(events);
Ray Milkey269ffb92014-04-03 14:43:30 -0700545 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800546
Ray Milkey269ffb92014-04-03 14:43:30 -0700547 //
548 // Cleanup
549 //
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700550 apiAddedMastershipEvents.clear();
551 apiRemovedMastershipEvents.clear();
Ray Milkey269ffb92014-04-03 14:43:30 -0700552 apiAddedSwitchEvents.clear();
553 apiRemovedSwitchEvents.clear();
554 apiAddedPortEvents.clear();
555 apiRemovedPortEvents.clear();
556 apiAddedLinkEvents.clear();
557 apiRemovedLinkEvents.clear();
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700558 apiAddedHostEvents.clear();
559 apiRemovedHostEvents.clear();
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800560 }
561
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700562 //
563 // Methods to update topology replica
564 //
565
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800566 /**
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700567 * Adds Switch Mastership event.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700568 *
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700569 * @param mastershipEvent the MastershipEvent to process.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700570 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700571 */
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700572 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700573 private boolean addMastershipEvent(MastershipEvent mastershipEvent) {
574 log.debug("Added Mastership event {}", mastershipEvent);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700575 lastAddMastershipEvents.put(mastershipEvent.getIDasByteBuffer(),
576 mastershipEvent);
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700577 apiAddedMastershipEvents.add(mastershipEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700578 return true;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700579 }
580
581 /**
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700582 * Removes Switch Mastership event.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700583 *
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700584 * @param mastershipEvent the MastershipEvent to process.
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700585 */
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700586 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700587 private void removeMastershipEvent(MastershipEvent mastershipEvent) {
588 log.debug("Removed Mastership event {}", mastershipEvent);
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700589 lastAddMastershipEvents.remove(mastershipEvent.getIDasByteBuffer());
Pavlin Radoslavovcac157d2014-07-31 13:54:08 -0700590 apiRemovedMastershipEvents.add(mastershipEvent);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700591 }
592
593 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700594 * Adds a switch to the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800595 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700596 * @param switchEvent the SwitchEvent with the switch to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700597 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800598 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700599 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700600 private boolean addSwitch(SwitchEvent switchEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700601 if (log.isDebugEnabled()) {
602 SwitchEvent sw = topology.getSwitchEvent(switchEvent.getDpid());
603 if (sw != null) {
604 log.debug("Update {}", switchEvent);
605 } else {
606 log.debug("Added {}", switchEvent);
607 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700608 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700609 topology.putSwitch(switchEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700610 apiAddedSwitchEvents.add(switchEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700611 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800612 }
613
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800614 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700615 * Removes a switch from the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700616 * <p/>
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700617 * It will call {@link #removePort(PortEvent)} for each ports on this
618 * switch.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800619 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700620 * @param switchEvent the SwitchEvent with the switch to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800621 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700622 @GuardedBy("topology.writeLock")
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800623 private void removeSwitch(SwitchEvent switchEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700624 final Dpid dpid = switchEvent.getDpid();
625
626 SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
627 if (swInTopo == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700628 log.warn("Switch {} already removed, ignoring", switchEvent);
629 return;
630 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800631
Ray Milkey269ffb92014-04-03 14:43:30 -0700632 //
633 // Remove all Ports on the Switch
634 //
635 ArrayList<PortEvent> portsToRemove = new ArrayList<>();
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700636 for (Port port : topology.getPorts(dpid)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700637 log.warn("Port {} on Switch {} should be removed prior to removing Switch. Removing Port now.",
638 port, switchEvent);
Yuta HIGUCHIcd14dda2014-07-24 09:57:22 -0700639 PortEvent portEvent = new PortEvent(port.getSwitchPort());
Ray Milkey269ffb92014-04-03 14:43:30 -0700640 portsToRemove.add(portEvent);
641 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700642 for (PortEvent portEvent : portsToRemove) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700643 removePort(portEvent);
Ray Milkeyb29e6262014-04-09 16:02:14 -0700644 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800645
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700646 log.debug("Removed {}", swInTopo);
647 topology.removeSwitch(dpid);
648 apiRemovedSwitchEvents.add(swInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800649 }
650
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800651 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700652 * Adds a port to the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800653 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700654 * @param portEvent the PortEvent with the port to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700655 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800656 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700657 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700658 private boolean addPort(PortEvent portEvent) {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700659 Switch sw = topology.getSwitch(portEvent.getDpid());
Ray Milkey269ffb92014-04-03 14:43:30 -0700660 if (sw == null) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700661 // Reordered event
Jonathan Hartf1675202014-05-23 14:59:07 -0700662 log.debug("{} reordered because switch is null", portEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700663 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700664 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800665
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700666 if (log.isDebugEnabled()) {
667 PortEvent port = topology.getPortEvent(portEvent.getSwitchPort());
668 if (port != null) {
669 log.debug("Update {}", portEvent);
670 } else {
671 log.debug("Added {}", portEvent);
672 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700673 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700674 topology.putPort(portEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700675 apiAddedPortEvents.add(portEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700676 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800677 }
678
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800679 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700680 * Removes a port from the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700681 * <p/>
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700682 * It will remove attachment points from each hosts on this port
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700683 * and call {@link #removeLink(LinkEvent)} for each links on this port.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800684 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700685 * @param portEvent the PortEvent with the port to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800686 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700687 @GuardedBy("topology.writeLock")
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800688 private void removePort(PortEvent portEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700689 SwitchEvent sw = topology.getSwitchEvent(portEvent.getDpid());
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 if (sw == null) {
691 log.warn("Parent Switch for Port {} already removed, ignoring",
692 portEvent);
693 return;
694 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800695
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700696 final SwitchPort switchPort = portEvent.getSwitchPort();
697 PortEvent portInTopo = topology.getPortEvent(switchPort);
698 if (portInTopo == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700699 log.warn("Port {} already removed, ignoring", portEvent);
700 return;
701 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800702
Ray Milkey269ffb92014-04-03 14:43:30 -0700703 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700704 // Remove all Host attachment points bound to this Port
Ray Milkey269ffb92014-04-03 14:43:30 -0700705 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700706 List<HostEvent> hostsToUpdate = new ArrayList<>();
707 for (Host host : topology.getHosts(switchPort)) {
708 log.debug("Removing Host {} on Port {}", host, portInTopo);
709 HostEvent hostEvent = topology.getHostEvent(host.getMacAddress());
710 hostsToUpdate.add(hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700711 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700712 for (HostEvent hostEvent : hostsToUpdate) {
713 HostEvent newHostEvent = new HostEvent(hostEvent);
714 newHostEvent.removeAttachmentPoint(switchPort);
715 newHostEvent.freeze();
716
717 // TODO should this event be fired inside #addHost?
718 if (newHostEvent.getAttachmentPoints().isEmpty()) {
719 // No more attachment point left -> remove Host
720 removeHost(hostEvent);
721 } else {
722 // Update Host
723 addHost(newHostEvent);
724 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700725 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800726
Ray Milkey269ffb92014-04-03 14:43:30 -0700727 //
728 // Remove all Links connected to the Port
729 //
730 Set<Link> links = new HashSet<>();
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700731 links.addAll(topology.getOutgoingLinks(switchPort));
732 links.addAll(topology.getIncomingLinks(switchPort));
Ray Milkey269ffb92014-04-03 14:43:30 -0700733 for (Link link : links) {
Ray Milkeyb29e6262014-04-09 16:02:14 -0700734 if (link == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700735 continue;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700736 }
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700737 LinkEvent linkEvent = topology.getLinkEvent(link.getLinkTuple());
738 if (linkEvent != null) {
739 log.debug("Removing Link {} on Port {}", link, portInTopo);
740 removeLink(linkEvent);
741 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700742 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800743
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700744 // Remove the Port from Topology
745 log.debug("Removed {}", portInTopo);
746 topology.removePort(switchPort);
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800747
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700748 apiRemovedPortEvents.add(portInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800749 }
750
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800751 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700752 * Adds a link to the topology replica.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700753 * <p/>
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700754 * It will remove attachment points from each hosts using the same ports.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800755 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700756 * @param linkEvent the LinkEvent with the link to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700757 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800758 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700759 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700760 private boolean addLink(LinkEvent linkEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700761 PortEvent srcPort = topology.getPortEvent(linkEvent.getSrc());
762 PortEvent dstPort = topology.getPortEvent(linkEvent.getDst());
Ray Milkey269ffb92014-04-03 14:43:30 -0700763 if ((srcPort == null) || (dstPort == null)) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700764 // Reordered event
Jonathan Hartf1675202014-05-23 14:59:07 -0700765 log.debug("{} reordered because {} port is null", linkEvent,
766 (srcPort == null) ? "src" : "dst");
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700767 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700768 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800769
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700770 //
771 // XXX domain knowledge: Sanity check: Port cannot have both Link and
772 // Host.
773 //
774 // FIXME: Potentially local replica may not be up-to-date yet due to
775 // Hazelcast delay.
776 // FIXME: May need to manage local truth and use them instead.
777 //
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700778 if (topology.getLinkEvent(linkEvent.getLinkTuple()) == null) {
779 // Only check for existing Host when adding new Link.
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700780 // Remove all Hosts attached to the ports on both ends
781
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700782 Set<HostEvent> hostsToUpdate =
783 new TreeSet<>(new Comparator<HostEvent>() {
784 // Comparison only using ID(=MAC)
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700785 @Override
786 public int compare(HostEvent o1, HostEvent o2) {
787 return Long.compare(o1.getMac().toLong(), o2.getMac().toLong());
788 }
789 });
790
791 List<SwitchPort> portsToCheck = Arrays.asList(
792 srcPort.getSwitchPort(),
793 dstPort.getSwitchPort());
794
795 // Enumerate Host which needs to be updated by this Link add event
796 for (SwitchPort port : portsToCheck) {
797 for (Host host : topology.getHosts(port)) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700798 log.error("Host {} on Port {} should have been removed prior to adding Link {}",
799 host, port, linkEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700800
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700801 HostEvent hostEvent =
802 topology.getHostEvent(host.getMacAddress());
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700803 hostsToUpdate.add(hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700804 }
805 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700806 // Remove attachment point from them
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700807 for (HostEvent hostEvent : hostsToUpdate) {
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700808 // Remove port from attachment point and update
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700809 HostEvent newHostEvent = new HostEvent(hostEvent);
810 newHostEvent.removeAttachmentPoint(srcPort.getSwitchPort());
811 newHostEvent.removeAttachmentPoint(dstPort.getSwitchPort());
812 newHostEvent.freeze();
813
814 // TODO should this event be fired inside #addHost?
815 if (newHostEvent.getAttachmentPoints().isEmpty()) {
816 // No more attachment point left -> remove Host
817 removeHost(hostEvent);
818 } else {
819 // Update Host
820 addHost(newHostEvent);
821 }
Ray Milkeyb29e6262014-04-09 16:02:14 -0700822 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700823 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800824
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700825 if (log.isDebugEnabled()) {
826 LinkEvent link = topology.getLinkEvent(linkEvent.getLinkTuple());
827 if (link != null) {
828 log.debug("Update {}", linkEvent);
829 } else {
830 log.debug("Added {}", linkEvent);
831 }
832 }
833 topology.putLink(linkEvent.freeze());
Ray Milkey269ffb92014-04-03 14:43:30 -0700834 apiAddedLinkEvents.add(linkEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700835 return true;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800836 }
837
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800838 /**
Yuta HIGUCHI7926ba32014-07-09 11:39:32 -0700839 * Removes a link from the topology replica.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800840 *
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700841 * @param linkEvent the LinkEvent with the link to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800842 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700843 @GuardedBy("topology.writeLock")
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800844 private void removeLink(LinkEvent linkEvent) {
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700845 Port srcPort = topology.getPort(linkEvent.getSrc().getDpid(),
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700846 linkEvent.getSrc().getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700847 if (srcPort == null) {
848 log.warn("Src Port for Link {} already removed, ignoring",
849 linkEvent);
850 return;
851 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800852
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700853 Port dstPort = topology.getPort(linkEvent.getDst().getDpid(),
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700854 linkEvent.getDst().getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700855 if (dstPort == null) {
856 log.warn("Dst Port for Link {} already removed, ignoring",
857 linkEvent);
858 return;
859 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800860
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700861 LinkEvent linkInTopo = topology.getLinkEvent(linkEvent.getLinkTuple(),
862 linkEvent.getType());
863 if (linkInTopo == null) {
864 log.warn("Link {} already removed, ignoring", linkEvent);
865 return;
Ray Milkey269ffb92014-04-03 14:43:30 -0700866 }
Jonathan Hart25bd53e2014-04-30 23:44:09 -0700867
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700868 if (log.isDebugEnabled()) {
869 // only do sanity check on debug level
870
871 Link linkIn = dstPort.getIncomingLink(linkEvent.getType());
872 if (linkIn == null) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700873 log.warn("Link {} already removed on destination Port",
874 linkEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700875 }
876 Link linkOut = srcPort.getOutgoingLink(linkEvent.getType());
877 if (linkOut == null) {
878 log.warn("Link {} already removed on src Port", linkEvent);
879 }
Jonathan Hart25bd53e2014-04-30 23:44:09 -0700880 }
Pavlin Radoslavov74986ce2014-02-20 13:17:20 -0800881
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700882 log.debug("Removed {}", linkInTopo);
883 topology.removeLink(linkEvent.getLinkTuple(), linkEvent.getType());
884 apiRemovedLinkEvents.add(linkInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800885 }
886
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800887 /**
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700888 * Adds a host to the topology replica.
Ray Milkey269ffb92014-04-03 14:43:30 -0700889 * <p/>
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700890 * TODO: Host-related work is incomplete.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800891 * TODO: Eventually, we might need to consider reordering
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700892 * or {@link #addLink(LinkEvent)} and {@link #addHost(HostEvent)} events
893 * on the same port.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800894 *
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700895 * @param hostEvent the HostEvent with the host to add.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700896 * @return true if the item was successfully added, otherwise false.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800897 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700898 @GuardedBy("topology.writeLock")
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700899 private boolean addHost(HostEvent hostEvent) {
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800900
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700901 // TODO Decide how to handle update scenario.
902 // If the new HostEvent has less attachment point compared to
903 // existing HostEvent, what should the event be?
904 // - AddHostEvent with some attachment point removed? (current behavior)
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800905
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700906 // create unfrozen copy
907 // for removing attachment points which already has a link
908 HostEvent modifiedHostEvent = new HostEvent(hostEvent);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800909
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700910 // Verify each attachment point
Ray Milkey269ffb92014-04-03 14:43:30 -0700911 boolean attachmentFound = false;
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700912 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
913 // XXX domain knowledge: Port must exist before Host
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700914 // but this knowledge cannot be pushed down to driver.
915
Ray Milkey269ffb92014-04-03 14:43:30 -0700916 // Attached Ports must exist
Yuta HIGUCHIb1e2ab72014-06-30 11:01:31 -0700917 Port port = topology.getPort(swp.getDpid(), swp.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -0700918 if (port == null) {
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700919 log.debug("{} reordered because port {} was not there",
920 hostEvent, swp);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700921 // Reordered event
922 return false; // should not continue if re-applying later
Ray Milkey269ffb92014-04-03 14:43:30 -0700923 }
924 // Attached Ports must not have Link
925 if (port.getOutgoingLink() != null ||
926 port.getIncomingLink() != null) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700927 log.warn("Link (Out:{},In:{}) exist on the attachment point. "
928 + "Ignoring this attachmentpoint ({}) from {}.",
929 port.getOutgoingLink(), port.getIncomingLink(),
930 swp, modifiedHostEvent);
931 // FIXME Should either reject, reorder this HostEvent,
932 // or remove attachment point from given HostEvent
933 // Removing attachment point from given HostEvent for now.
934 modifiedHostEvent.removeAttachmentPoint(swp);
Ray Milkey269ffb92014-04-03 14:43:30 -0700935 continue;
936 }
937
Ray Milkey269ffb92014-04-03 14:43:30 -0700938 attachmentFound = true;
939 }
940
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700941 // Update the host in the topology
Ray Milkey269ffb92014-04-03 14:43:30 -0700942 if (attachmentFound) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700943 if (modifiedHostEvent.getAttachmentPoints().isEmpty()) {
944 log.warn("No valid attachment point left. Ignoring."
Pavlin Radoslavov8a44b782014-08-07 12:53:27 -0700945 + "original: {}, modified: {}",
946 hostEvent, modifiedHostEvent);
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700947 // TODO Should we call #removeHost to trigger remove event?
948 // only if this call is update.
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700949 return false;
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700950 }
951
952 if (log.isDebugEnabled()) {
953 HostEvent host = topology.getHostEvent(hostEvent.getMac());
954 if (host != null) {
955 log.debug("Update {}", modifiedHostEvent);
956 } else {
957 log.debug("Added {}", modifiedHostEvent);
958 }
959 }
960 topology.putHost(modifiedHostEvent.freeze());
961 apiAddedHostEvents.add(modifiedHostEvent);
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700962 return true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700963 }
Pavlin Radoslavovd7b792e2014-08-01 02:47:47 -0700964 return false;
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800965 }
966
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800967 /**
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700968 * Removes a host from the topology replica.
Ray Milkey269ffb92014-04-03 14:43:30 -0700969 * <p/>
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700970 * TODO: Host-related work is incomplete.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800971 *
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700972 * @param hostEvent the Host Event with the host to remove.
Pavlin Radoslavov734ff5a2014-02-26 10:20:43 -0800973 */
Yuta HIGUCHIbc67a052014-06-30 10:37:09 -0700974 @GuardedBy("topology.writeLock")
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700975 private void removeHost(HostEvent hostEvent) {
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700976
977 final MACAddress mac = hostEvent.getMac();
978 HostEvent hostInTopo = topology.getHostEvent(mac);
979 if (hostInTopo == null) {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700980 log.warn("Host {} already removed, ignoring", hostEvent);
Ray Milkey269ffb92014-04-03 14:43:30 -0700981 return;
982 }
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800983
Yuta HIGUCHI8b389a72014-07-18 13:50:00 -0700984 log.debug("Removed {}", hostInTopo);
985 topology.removeHost(mac);
986 apiRemovedHostEvents.add(hostInTopo);
Pavlin Radoslavov3c9cc552014-02-20 09:58:38 -0800987 }
Jonathan Hart062a2e82014-02-03 09:41:57 -0800988}