blob: ff05cc3dedc8fc54ffe8ad2380e92cdb27fbf2e9 [file] [log] [blame]
Jonathan Hart472062d2014-04-03 10:56:48 -07001package net.onrc.onos.core.topology;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08002
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -07003import java.nio.ByteBuffer;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08004import java.util.ArrayList;
5import java.util.Collection;
Pavlin Radoslavov24409672014-08-20 16:45:11 -07006import java.util.HashMap;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -07007import java.util.LinkedList;
Jonathan Hart369875b2014-02-13 10:00:31 -08008import java.util.List;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -08009import java.util.Map;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070010import java.util.concurrent.BlockingQueue;
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070011import java.util.concurrent.ConcurrentHashMap;
12import java.util.concurrent.ConcurrentMap;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070013import java.util.concurrent.LinkedBlockingQueue;
Jonathan Hart369875b2014-02-13 10:00:31 -080014import java.util.concurrent.TimeUnit;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080015
16import net.floodlightcontroller.core.IFloodlightProviderService;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070017import net.floodlightcontroller.core.IFloodlightProviderService.Role;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080018import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070019import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
20import net.floodlightcontroller.core.IOFSwitchListener;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080021import net.floodlightcontroller.core.module.FloodlightModuleContext;
22import net.floodlightcontroller.core.module.FloodlightModuleException;
23import net.floodlightcontroller.core.module.IFloodlightModule;
24import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart369875b2014-02-13 10:00:31 -080025import net.floodlightcontroller.core.util.SingletonTask;
26import net.floodlightcontroller.threadpool.IThreadPoolService;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070027
28import net.onrc.onos.api.batchoperation.BatchOperationEntry;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070029import net.onrc.onos.core.datagrid.IDatagridService;
30import net.onrc.onos.core.datagrid.IEventChannel;
Jonathan Hart03102132014-07-01 23:22:04 -070031import net.onrc.onos.core.hostmanager.Host;
32import net.onrc.onos.core.hostmanager.IHostListener;
33import net.onrc.onos.core.hostmanager.IHostService;
Jonathan Hart23701d12014-04-03 10:45:48 -070034import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryListener;
35import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart284e70f2014-07-05 12:32:51 -070036import net.onrc.onos.core.linkdiscovery.Link;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070037import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070038import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070039import net.onrc.onos.core.registry.RegistryException;
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -070040import net.onrc.onos.core.util.Dpid;
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070041import net.onrc.onos.core.util.OnosInstanceId;
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -070042import net.onrc.onos.core.util.PortNumberUtils;
Yuta HIGUCHI5c8cbeb2014-06-27 11:13:48 -070043import net.onrc.onos.core.util.SwitchPort;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080044
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070045import org.apache.commons.lang3.concurrent.ConcurrentUtils;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070046import org.projectfloodlight.openflow.protocol.OFPortDesc;
47import org.projectfloodlight.openflow.util.HexString;
Toshio Koide2f570c12014-02-06 16:55:32 -080048import org.slf4j.Logger;
49import org.slf4j.LoggerFactory;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080050
Jonathan Hart88770672014-04-02 18:08:30 -070051/**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070052 * Class for publishing topology-related events.
53 *
54 * The events are received from the discovery modules, reformatted and
55 * published to the other ONOS instances.
56 *
57 * TODO: Add a synchronization mechanism when publishing the events to
58 * preserve the ordering and to avoid mismatch in the local "published" state,
59 * because each of the caller (the discovery modules) might be running
60 * on a different thread.
Jonathan Hart88770672014-04-02 18:08:30 -070061 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070062public class TopologyPublisher implements IOFSwitchListener,
Ray Milkey269ffb92014-04-03 14:43:30 -070063 ILinkDiscoveryListener,
64 IFloodlightModule,
Pavlin Radoslavov24409672014-08-20 16:45:11 -070065 IHostListener,
66 ITopologyPublisherService {
Jonathan Hart88770672014-04-02 18:08:30 -070067 private static final Logger log =
Jonathan Harte37e4e22014-05-13 19:12:02 -070068 LoggerFactory.getLogger(TopologyPublisher.class);
Yuta HIGUCHIcb951982014-02-11 13:31:44 -080069
Jonathan Hart88770672014-04-02 18:08:30 -070070 private IFloodlightProviderService floodlightProvider;
71 private ILinkDiscoveryService linkDiscovery;
72 private IControllerRegistryService registryService;
Jonathan Harte37e4e22014-05-13 19:12:02 -070073 private ITopologyService topologyService;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070074 private IDatagridService datagridService;
Toshio Koide2f570c12014-02-06 16:55:32 -080075
Jonathan Hart03102132014-07-01 23:22:04 -070076 private IHostService hostService;
Jonathan Hartb3e1b052014-04-02 16:01:12 -070077
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -070078 private MutableTopology mutableTopology;
Jonathan Hartb3e1b052014-04-02 16:01:12 -070079
Jonathan Hart88770672014-04-02 18:08:30 -070080 private static final String ENABLE_CLEANUP_PROPERTY = "EnableCleanup";
81 private boolean cleanupEnabled = true;
82 private static final int CLEANUP_TASK_INTERVAL = 60; // in seconds
83 private SingletonTask cleanupTask;
Pavlin Radoslavov24409672014-08-20 16:45:11 -070084 private DelayedOperationsHandler delayedOperationsHandler =
85 new DelayedOperationsHandler();
Toshio Koide2f570c12014-02-06 16:55:32 -080086
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070087 private IEventChannel<byte[], TopologyEvent> eventChannel;
88
89 //
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070090 // Local state for keeping track of locally published events so we can
91 // cleanup properly when an entry is removed.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070092 //
93 // We keep all Port, (incoming) Link and Host events per Switch DPID:
94 // - If a switch goes down, we remove all corresponding Port, Link and
95 // Host events.
96 // - If a port on a switch goes down, we remove all corresponding Link
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070097 // and Host events attached to this port.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070098 //
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -070099 // TODO: What to do if the Mastership changes?
100 // - Cleanup state from publishedFoo maps, but do not send REMOVE events?
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700101 //
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700102 private ConcurrentMap<Dpid, MastershipEvent> publishedMastershipEvents =
103 new ConcurrentHashMap<>();
104 private ConcurrentMap<Dpid, SwitchEvent> publishedSwitchEvents =
105 new ConcurrentHashMap<>();
106 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, PortEvent>>
107 publishedPortEvents = new ConcurrentHashMap<>();
108 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, LinkEvent>>
109 publishedLinkEvents = new ConcurrentHashMap<>();
110 private ConcurrentMap<Dpid, ConcurrentMap<ByteBuffer, HostEvent>>
111 publishedHostEvents = new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700112
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700113 private BlockingQueue<TopologyBatchOperation> delayedOperations =
114 new LinkedBlockingQueue<>();
115
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700116
Jonathan Hart369875b2014-02-13 10:00:31 -0800117 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700118 * Gets the ONOS Instance ID.
119 *
120 * @return the ONOS Instance ID.
121 */
122 private OnosInstanceId getOnosInstanceId() {
123 return registryService.getOnosInstanceId();
124 }
125
126 /**
127 * Cleanup old switches from the topology. Old switches are those which
128 * have no controller in the registry.
129 *
130 * TODO: The overall switch cleanup mechanism needs refactoring/redesign.
Jonathan Hart369875b2014-02-13 10:00:31 -0800131 */
132 private class SwitchCleanup implements ControlChangeCallback, Runnable {
133 @Override
134 public void run() {
135 String old = Thread.currentThread().getName();
136 Thread.currentThread().setName("SwitchCleanup@" + old);
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700137
Jonathan Hart369875b2014-02-13 10:00:31 -0800138 try {
Jonathan Hart88770672014-04-02 18:08:30 -0700139 if (log.isTraceEnabled()) {
140 log.trace("Running cleanup thread");
141 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800142 switchCleanup();
Jonathan Hart369875b2014-02-13 10:00:31 -0800143 } finally {
144 cleanupTask.reschedule(CLEANUP_TASK_INTERVAL,
Jonathan Hart88770672014-04-02 18:08:30 -0700145 TimeUnit.SECONDS);
Jonathan Hart369875b2014-02-13 10:00:31 -0800146 Thread.currentThread().setName(old);
147 }
148 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700149
Jonathan Hart88770672014-04-02 18:08:30 -0700150 /**
151 * First half of the switch cleanup operation. This method will attempt
152 * to get control of any switch it sees without a controller via the
153 * registry.
154 */
Jonathan Hart369875b2014-02-13 10:00:31 -0800155 private void switchCleanup() {
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700156 Iterable<Switch> switches = mutableTopology.getSwitches();
Jonathan Hart369875b2014-02-13 10:00:31 -0800157
Jonathan Hart88770672014-04-02 18:08:30 -0700158 if (log.isTraceEnabled()) {
159 log.trace("Checking for inactive switches");
160 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700161 // For each switch check if a controller exists in controller
162 // registry
Ray Milkey269ffb92014-04-03 14:43:30 -0700163 for (Switch sw : switches) {
Praseed Balakrishnane82adc62014-08-04 10:59:24 -0700164 // FIXME How to handle case where Switch has never been
165 // registered to ZK
166 if (sw.getConfigState() == ConfigState.CONFIGURED) {
167 continue;
168 }
Jonathan Hart88770672014-04-02 18:08:30 -0700169 try {
170 String controller =
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700171 registryService.getControllerForSwitch(sw.getDpid().value());
Jonathan Hart88770672014-04-02 18:08:30 -0700172 if (controller == null) {
173 log.debug("Requesting control to set switch {} INACTIVE",
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700174 sw.getDpid());
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700175 registryService.requestControl(sw.getDpid().value(),
176 this);
Jonathan Hart88770672014-04-02 18:08:30 -0700177 }
178 } catch (RegistryException e) {
179 log.error("Caught RegistryException in cleanup thread", e);
180 }
181 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800182 }
183
Jonathan Hart88770672014-04-02 18:08:30 -0700184 /**
185 * Second half of the switch cleanup operation. If the registry grants
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700186 * control of a switch, we can be sure no other instance is writing
187 * this switch to the topology, so we can remove it now.
188 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700189 * @param dpid the dpid of the switch we requested control for
Jonathan Hart88770672014-04-02 18:08:30 -0700190 * @param hasControl whether we got control or not
191 */
192 @Override
193 public void controlChanged(long dpid, boolean hasControl) {
194 if (hasControl) {
195 log.debug("Got control to set switch {} INACTIVE",
196 HexString.toHexString(dpid));
Jonathan Harte02cf542014-04-02 16:24:44 -0700197
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700198 SwitchEvent switchEvent = new SwitchEvent(new Dpid(dpid));
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700199 publishRemoveSwitchEvent(switchEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700200 registryService.releaseControl(dpid);
201 }
202 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800203 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800204
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700205 /**
206 * A class to deal with Topology Operations that couldn't be pushed
207 * to the Global Log writer, because they need to be delayed.
208 * For example, a link cannot be pushed before the switches on both
209 * ends are in the Global Log.
210 *
211 * TODO: This is an ugly hack that should go away: right now we have to
212 * keep trying periodically.
213 * TODO: Currently, we retry only ADD Link Events, everything else
214 * is thrown away.
215 */
216 private class DelayedOperationsHandler extends Thread {
217 private static final long RETRY_INTERVAL_MS = 10; // 10ms
218
219 @Override
220 public void run() {
221 List<TopologyBatchOperation> operations = new LinkedList<>();
222
223 this.setName("TopologyPublisher.DelayedOperationsHandler " +
224 this.getId());
225 //
226 // The main loop
227 //
228 while (true) {
229 try {
230 //
231 // Block-waiting for an operation to be added, sleep
232 // and try to publish it again.
233 //
234 TopologyBatchOperation firstTbo = delayedOperations.take();
235 Thread.sleep(RETRY_INTERVAL_MS);
236 operations.add(firstTbo);
237 delayedOperations.drainTo(operations);
238
239 // Retry only the appropriate operations
240 for (TopologyBatchOperation tbo : operations) {
241 for (BatchOperationEntry<
242 TopologyBatchOperation.Operator,
243 TopologyEvent> boe : tbo.getOperations()) {
244 TopologyBatchOperation.Operator oper =
245 boe.getOperator();
246 switch (oper) {
247 case ADD:
248 TopologyEvent topologyEvent = boe.getTarget();
249 LinkEvent linkEvent =
250 topologyEvent.getLinkEvent();
251 //
252 // Test whether the Link Event still can be
253 // published.
254 // TODO: The implementation below has a bug:
255 // If it happens that the same Link Event was
256 // removed in the middle of checking, we might
257 // incorrectly publish it again from here.
258 //
259 if (linkEvent == null) {
260 break;
261 }
262 ConcurrentMap<ByteBuffer, LinkEvent>
263 linkEvents = publishedLinkEvents.get(
264 linkEvent.getDst().getDpid());
265 if (linkEvents == null) {
266 break;
267 }
268 if (linkEvents.get(linkEvent.getIDasByteBuffer()) == null) {
269 break;
270 }
271 publishAddLinkEvent(linkEvent);
272 break;
273 case REMOVE:
274 break;
275 default:
276 log.error("Unknown Topology Batch Operation {}", oper);
277 break;
278 }
279 }
280 }
281 } catch (InterruptedException exception) {
282 log.debug("Exception processing delayed operations: ",
283 exception);
284 }
285 }
286 }
287 }
288
Jonathan Hart88770672014-04-02 18:08:30 -0700289 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700290 public void linkAdded(Link link) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700291 LinkEvent linkEvent = new LinkEvent(
Jonathan Hart284e70f2014-07-05 12:32:51 -0700292 new SwitchPort(link.getSrc(), link.getSrcPort()),
293 new SwitchPort(link.getDst(), link.getDstPort()));
294
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700295 // FIXME should be merging, with existing attrs, etc..
296 // TODO define attr name as constant somewhere.
297 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700298 linkEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700299 TopologyElement.TYPE_PACKET_LAYER);
Praseed Balakrishnan2aa6c0b2014-07-17 11:42:05 -0700300 linkEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
301 ConfigState.NOT_CONFIGURED.toString());
302 linkEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
303 AdminStatus.ACTIVE.toString());
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700304 linkEvent.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700305
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700306 publishAddLinkEvent(linkEvent);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700307 }
308
309 @Override
310 public void linkRemoved(Link link) {
311 LinkEvent linkEvent = new LinkEvent(
312 new SwitchPort(link.getSrc(), link.getSrcPort()),
313 new SwitchPort(link.getDst(), link.getDstPort()));
314
315 // FIXME should be merging, with existing attrs, etc..
316 // TODO define attr name as constant somewhere.
317 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700318 linkEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700319 TopologyElement.TYPE_PACKET_LAYER);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700320 linkEvent.freeze();
321
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700322 publishRemoveLinkEvent(linkEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700323 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800324
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700325 /* *****************
326 * IOFSwitchListener
327 * *****************/
328
Jonathan Hart88770672014-04-02 18:08:30 -0700329 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700330 public void switchActivatedMaster(long swId) {
331 IOFSwitch sw = floodlightProvider.getSwitch(swId);
332 final Dpid dpid = new Dpid(swId);
333 if (sw == null) {
334 log.warn("Added switch not available {} ", dpid);
335 return;
336 }
337
338 controllerRoleChanged(dpid, Role.MASTER);
339
340 SwitchEvent switchEvent = new SwitchEvent(dpid);
341 // FIXME should be merging, with existing attrs, etc..
342 // TODO define attr name as constant somewhere.
343 // TODO populate appropriate attributes.
344 switchEvent.createStringAttribute(TopologyElement.TYPE,
345 TopologyElement.TYPE_PACKET_LAYER);
346 switchEvent.createStringAttribute("ConnectedSince",
347 sw.getConnectedSince().toString());
348 switchEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
349 ConfigState.NOT_CONFIGURED.toString());
350 switchEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
351 AdminStatus.ACTIVE.toString());
352 switchEvent.freeze();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700353 // The Port events
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700354 List<PortEvent> portEvents = new ArrayList<PortEvent>();
355 for (OFPortDesc port : sw.getPorts()) {
356 PortEvent portEvent = new PortEvent(dpid,
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700357 PortNumberUtils.openFlow(port));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700358 // FIXME should be merging, with existing attrs, etc..
359 // TODO define attr name as constant somewhere.
360 // TODO populate appropriate attributes.
361 portEvent.createStringAttribute("name", port.getName());
362 portEvent.createStringAttribute(TopologyElement.TYPE,
363 TopologyElement.TYPE_PACKET_LAYER);
364 portEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
365 ConfigState.NOT_CONFIGURED.toString());
366 portEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
367 AdminStatus.ACTIVE.toString());
368
369 portEvent.freeze();
370 portEvents.add(portEvent);
371 }
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700372 publishAddSwitchEvent(switchEvent, portEvents);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700373 }
374
375 @Override
376 public void switchActivatedEqual(long swId) {
377 final Dpid dpid = new Dpid(swId);
378 controllerRoleChanged(dpid, Role.EQUAL);
379 }
380
381 @Override
382 public void switchMasterToEqual(long swId) {
383 final Dpid dpid = new Dpid(swId);
384 controllerRoleChanged(dpid, Role.EQUAL);
385 }
386
387 @Override
388 public void switchEqualToMaster(long swId) {
389 // for now treat as switchActivatedMaster
390 switchActivatedMaster(swId);
391 }
392
393 @Override
394 public void switchDisconnected(long swId) {
395 final Dpid dpid = new Dpid(swId);
396
397 log.debug("Local switch disconnected: dpid = {} role = {}", dpid);
398
399 Role role = Role.SLAVE; // TODO: Should be Role.UNKNOWN
400
401 MastershipEvent mastershipEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700402 new MastershipEvent(dpid, getOnosInstanceId(), role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700403 // FIXME should be merging, with existing attrs, etc..
404 // TODO define attr name as constant somewhere.
405 // TODO populate appropriate attributes.
406 mastershipEvent.createStringAttribute(TopologyElement.TYPE,
407 TopologyElement.TYPE_ALL_LAYERS);
408 mastershipEvent.freeze();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700409 publishRemoveSwitchMastershipEvent(mastershipEvent);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700410 }
411
412 @Override
413 public void switchPortChanged(long swId, OFPortDesc port,
414 PortChangeType changeType) {
415 switch (changeType) {
416 case ADD:
417 switchPortAdded(swId, port);
418 break;
419 case DELETE:
420 switchPortRemoved(swId, port);
421 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700422 case UP:
Pavlin Radoslavov7946c612014-08-13 15:06:00 -0700423 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
424 switchPortAdded(swId, port);
425 break;
426 case DOWN:
427 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
428 switchPortRemoved(swId, port);
429 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700430 case OTHER_UPDATE:
431 default:
432 // XXX S what is the right set of port change handlers?
433 log.debug("Topology publisher does not handle these port updates: {}",
434 changeType);
435 }
436 }
437
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700438 /**
439 * Prepares an event for adding a port on a switch.
440 *
441 * @param switchId the switch ID (DPID)
442 * @param port the port to add
443 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700444 private void switchPortAdded(long switchId, OFPortDesc port) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700445 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700446 final PortEvent portEvent = new PortEvent(dpid,
447 PortNumberUtils.openFlow(port));
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700448 // FIXME should be merging, with existing attrs, etc..
449 // TODO define attr name as constant somewhere.
450 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700451 portEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700452 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700453 portEvent.createStringAttribute("name", port.getName());
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700454 portEvent.freeze();
455
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700456 publishAddPortEvent(portEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700457 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800458
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700459 /**
460 * Prepares an event for removing a port on a switch.
461 *
462 * @param switchId the switch ID (DPID)
463 * @param port the port to remove
464 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700465 private void switchPortRemoved(long switchId, OFPortDesc port) {
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700466 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700467
Yuta HIGUCHIa507baf2014-08-22 13:42:40 -0700468 final PortEvent portEvent = new PortEvent(dpid,
469 PortNumberUtils.openFlow(port));
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700470 // FIXME should be merging, with existing attrs, etc..
471 // TODO define attr name as constant somewhere.
472 // TODO populate appropriate attributes.
473 portEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700474 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700475 portEvent.createStringAttribute("name", port.getName());
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700476 portEvent.freeze();
477
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700478 publishRemovePortEvent(portEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700479 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800480
Jonathan Hart88770672014-04-02 18:08:30 -0700481 @Override
Jonathan Hart88770672014-04-02 18:08:30 -0700482 public String getName() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700483 return "topologyPublisher";
Jonathan Hart88770672014-04-02 18:08:30 -0700484 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800485
Jonathan Hart88770672014-04-02 18:08:30 -0700486 /* *****************
487 * IFloodlightModule
488 * *****************/
Toshio Koide2f570c12014-02-06 16:55:32 -0800489
Jonathan Hart88770672014-04-02 18:08:30 -0700490 @Override
491 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700492 List<Class<? extends IFloodlightService>> services =
493 new ArrayList<Class<? extends IFloodlightService>>();
494 services.add(ITopologyPublisherService.class);
495 return services;
Jonathan Hart88770672014-04-02 18:08:30 -0700496 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800497
Jonathan Hart88770672014-04-02 18:08:30 -0700498 @Override
499 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700500 getServiceImpls() {
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700501 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
502 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
503 impls.put(ITopologyPublisherService.class, this);
504 return impls;
Jonathan Hart88770672014-04-02 18:08:30 -0700505 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800506
Jonathan Hart88770672014-04-02 18:08:30 -0700507 @Override
508 public Collection<Class<? extends IFloodlightService>>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700509 getModuleDependencies() {
Jonathan Hart88770672014-04-02 18:08:30 -0700510 Collection<Class<? extends IFloodlightService>> l =
511 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800512 l.add(IFloodlightProviderService.class);
513 l.add(ILinkDiscoveryService.class);
Jonathan Hart369875b2014-02-13 10:00:31 -0800514 l.add(IThreadPoolService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800515 l.add(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700516 l.add(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700517 l.add(ITopologyService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700518 l.add(IHostService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800519 return l;
Jonathan Hart88770672014-04-02 18:08:30 -0700520 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800521
Jonathan Hart88770672014-04-02 18:08:30 -0700522 @Override
523 public void init(FloodlightModuleContext context)
524 throws FloodlightModuleException {
525 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
526 linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
527 registryService = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700528 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700529 hostService = context.getServiceImpl(IHostService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700530 topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart88770672014-04-02 18:08:30 -0700531 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800532
Jonathan Hart88770672014-04-02 18:08:30 -0700533 @Override
534 public void startUp(FloodlightModuleContext context) {
535 floodlightProvider.addOFSwitchListener(this);
536 linkDiscovery.addListener(this);
Jonathan Hart03102132014-07-01 23:22:04 -0700537 hostService.addHostListener(this);
Toshio Koide2f570c12014-02-06 16:55:32 -0800538
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700539 eventChannel = datagridService.createChannel(
540 TopologyManager.EVENT_CHANNEL_NAME,
541 byte[].class,
542 TopologyEvent.class);
543
Yuta HIGUCHId92b10c2014-08-25 09:30:28 -0700544 mutableTopology = topologyService.getTopology();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700545
Jonathan Hart88770672014-04-02 18:08:30 -0700546 // Run the cleanup thread
547 String enableCleanup =
548 context.getConfigParams(this).get(ENABLE_CLEANUP_PROPERTY);
549 if (enableCleanup != null
550 && enableCleanup.equalsIgnoreCase("false")) {
551 cleanupEnabled = false;
552 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700553
Jonathan Hart88770672014-04-02 18:08:30 -0700554 log.debug("Cleanup thread is {}enabled", (cleanupEnabled) ? "" : "not ");
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700555
Jonathan Hart88770672014-04-02 18:08:30 -0700556 if (cleanupEnabled) {
557 IThreadPoolService threadPool =
558 context.getServiceImpl(IThreadPoolService.class);
559 cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
560 new SwitchCleanup());
561 // Run the cleanup task immediately on startup
562 cleanupTask.reschedule(0, TimeUnit.SECONDS);
563 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700564
565 // Run the Delayed Operations Handler thread
566 delayedOperationsHandler.start();
Jonathan Hart88770672014-04-02 18:08:30 -0700567 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700568
Jonathan Hart88770672014-04-02 18:08:30 -0700569 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700570 public void hostAdded(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700571 log.debug("Host added with MAC {}", host.getMacAddress());
TeruUd1c5b652014-03-24 13:58:46 -0700572
Jonathan Hart03102132014-07-01 23:22:04 -0700573 SwitchPort sp = new SwitchPort(host.getSwitchDPID(), host.getSwitchPort());
Jonathan Hart88770672014-04-02 18:08:30 -0700574 List<SwitchPort> spLists = new ArrayList<SwitchPort>();
575 spLists.add(sp);
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700576 HostEvent event = new HostEvent(host.getMacAddress());
Jonathan Hart88770672014-04-02 18:08:30 -0700577 event.setAttachmentPoints(spLists);
Jonathan Hart03102132014-07-01 23:22:04 -0700578 event.setLastSeenTime(host.getLastSeenTimestamp().getTime());
Jonathan Hart88770672014-04-02 18:08:30 -0700579 // Does not use vlan info now.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700580 event.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700581
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700582 publishAddHostEvent(event);
Jonathan Hart88770672014-04-02 18:08:30 -0700583 }
TeruUd1c5b652014-03-24 13:58:46 -0700584
Jonathan Hart88770672014-04-02 18:08:30 -0700585 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700586 public void hostRemoved(Host host) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700587 log.debug("Host removed with MAC {}", host.getMacAddress());
588
589 //
590 // Remove all previously added HostEvent for this MAC address
591 //
592 // TODO: Currently, the caller of hostRemoved() might not include
593 // the correct set of Attachment Points in the HostEvent entry itself.
594 // Also, we might have multiple HostEvent entries for the same
595 // host (MAC address), each containing a single (different) Attachment
596 // Point.
597 // Hence, here we have to cleanup all HostEvent entries for this
598 // particular host, based on its MAC address.
599 //
600 List<HostEvent> removeHostEvents = new LinkedList<>();
601 for (ConcurrentMap<ByteBuffer, HostEvent> cm : publishedHostEvents.values()) {
602 for (HostEvent hostEvent : cm.values()) {
603 if (hostEvent.getMac().equals(host.getMacAddress())) {
604 removeHostEvents.add(hostEvent);
605 }
606 }
607 }
608 for (HostEvent event : removeHostEvents) {
609 publishRemoveHostEvent(event);
610 }
Jonathan Hart88770672014-04-02 18:08:30 -0700611 }
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700612
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700613 @Override
614 public boolean publish(TopologyBatchOperation tbo) {
615 publishTopologyOperations(tbo);
616 return true;
617 }
618
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700619 /**
620 * Prepares the Controller role changed event for a switch.
621 *
622 * @param dpid the switch DPID
623 * @param role the new role of the controller
624 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700625 private void controllerRoleChanged(Dpid dpid, Role role) {
626 log.debug("Local switch controller mastership role changed: dpid = {} role = {}",
627 dpid, role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700628 MastershipEvent mastershipEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700629 new MastershipEvent(dpid, getOnosInstanceId(), role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700630 // FIXME should be merging, with existing attrs, etc..
631 // TODO define attr name as constant somewhere.
632 // TODO populate appropriate attributes.
633 mastershipEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700634 TopologyElement.TYPE_ALL_LAYERS);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700635 mastershipEvent.freeze();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700636 publishAddSwitchMastershipEvent(mastershipEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700637 }
638
639 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700640 * Publishes ADD Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700641 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700642 * @param mastershipEvent the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700643 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700644 private void publishAddSwitchMastershipEvent(
645 MastershipEvent mastershipEvent) {
646 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700647 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700648 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700649 new TopologyEvent(mastershipEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700650 tbo.appendAddOperation(topologyEvent);
651 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700652 publishedMastershipEvents.put(mastershipEvent.getDpid(),
653 mastershipEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700654 }
655
656 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700657 * Publishes REMOVE Mastership Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700658 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700659 * @param mastershipEvent the mastership event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700660 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700661 private void publishRemoveSwitchMastershipEvent(
662 MastershipEvent mastershipEvent) {
663 if (publishedMastershipEvents.get(mastershipEvent.getDpid()) == null) {
664 return; // Nothing to do
665 }
666
667 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700668 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700669 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700670 new TopologyEvent(mastershipEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700671 tbo.appendRemoveOperation(topologyEvent);
672 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700673 publishedMastershipEvents.remove(mastershipEvent.getDpid());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700674 }
675
676 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700677 * Publishes ADD Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700678 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700679 * @param switchEvent the switch event to publish
680 * @param portEvents the corresponding port events for the switch to
681 * publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700682 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700683 private void publishAddSwitchEvent(SwitchEvent switchEvent,
684 Collection<PortEvent> portEvents) {
685 if (!registryService.hasControl(switchEvent.getOriginDpid().value())) {
686 log.debug("Not the master for switch {}. Suppressed switch add event {}.",
687 switchEvent.getOriginDpid(), switchEvent);
688 return;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700689 }
690
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700691 // Keep track of the old Port Events that should be removed
692 ConcurrentMap<ByteBuffer, PortEvent> oldPortEvents =
693 publishedPortEvents.get(switchEvent.getDpid());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700694 if (oldPortEvents == null) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700695 oldPortEvents = new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700696 }
697
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700698 // Publish the information for the switch
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700699 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700700 TopologyEvent topologyEvent =
701 new TopologyEvent(switchEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700702 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700703
704 // Publish the information for each port
705 ConcurrentMap<ByteBuffer, PortEvent> newPortEvents =
706 new ConcurrentHashMap<>();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700707 for (PortEvent portEvent : portEvents) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700708 topologyEvent =
709 new TopologyEvent(portEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700710 tbo.appendAddOperation(topologyEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700711
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700712 ByteBuffer id = portEvent.getIDasByteBuffer();
713 newPortEvents.put(id, portEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700714 oldPortEvents.remove(id);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700715 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700716 publishTopologyOperations(tbo);
717 publishedSwitchEvents.put(switchEvent.getDpid(), switchEvent);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700718 publishedPortEvents.put(switchEvent.getDpid(), newPortEvents);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700719
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700720 // Cleanup for each of the old removed port
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700721 for (PortEvent portEvent : oldPortEvents.values()) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700722 publishRemovePortEvent(portEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700723 }
724 }
725
726 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700727 * Publishes REMOVE Switch Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700728 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700729 * @param switchEvent the switch event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700730 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700731 private void publishRemoveSwitchEvent(SwitchEvent switchEvent) {
732 //
733 // TODO: Removed the check for now, because currently this method is
734 // also called by the SwitchCleanup thread, and in that case
735 // the Switch Event was published by some other ONOS instance.
736 //
737 /*
738 if (publishedSwitchEvents.get(switchEvent.getDpid()) == null) {
739 return; // Nothing to do
740 }
741 */
742
743 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700744 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700745 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700746 new TopologyEvent(switchEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700747 tbo.appendRemoveOperation(topologyEvent);
748 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700749 publishedSwitchEvents.remove(switchEvent.getDpid());
750
751 // Cleanup for each port
752 ConcurrentMap<ByteBuffer, PortEvent> portEvents =
753 publishedPortEvents.get(switchEvent.getDpid());
754 if (portEvents != null) {
755 for (PortEvent portEvent : portEvents.values()) {
756 publishRemovePortEvent(portEvent);
757 }
758 }
759
760 publishedPortEvents.remove(switchEvent.getDpid());
761 publishedLinkEvents.remove(switchEvent.getDpid());
762 publishedHostEvents.remove(switchEvent.getDpid());
763 }
764
765 /**
766 * Publishes ADD Port Event.
767 *
768 * @param portEvent the port event to publish
769 */
770 private void publishAddPortEvent(PortEvent portEvent) {
771 if (!registryService.hasControl(portEvent.getOriginDpid().value())) {
772 log.debug("Not the master for switch {}. Suppressed port add event {}.",
773 portEvent.getOriginDpid(), portEvent);
774 return;
775 }
776
777 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700778 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700779 TopologyEvent topologyEvent =
780 new TopologyEvent(portEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700781 tbo.appendAddOperation(topologyEvent);
782 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700783
784 // Store the new Port Event in the local cache
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700785 ConcurrentMap<ByteBuffer, PortEvent> portEvents =
786 ConcurrentUtils.putIfAbsent(publishedPortEvents,
787 portEvent.getDpid(),
788 new ConcurrentHashMap<ByteBuffer, PortEvent>());
789 portEvents.put(portEvent.getIDasByteBuffer(), portEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700790 }
791
792 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700793 * Publishes REMOVE Port Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700794 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700795 * @param portEvent the port event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700796 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700797 private void publishRemovePortEvent(PortEvent portEvent) {
798 ConcurrentMap<ByteBuffer, PortEvent> portEvents =
799 publishedPortEvents.get(portEvent.getDpid());
800 if (portEvents == null) {
801 return; // Nothing to do
802 }
803 if (portEvents.get(portEvent.getIDasByteBuffer()) == null) {
804 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700805 }
806
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700807 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700808 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700809 TopologyEvent topologyEvent =
810 new TopologyEvent(portEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700811 tbo.appendRemoveOperation(topologyEvent);
812 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700813
814 // Cleanup for the incoming link(s)
815 ConcurrentMap<ByteBuffer, LinkEvent> linkEvents =
816 publishedLinkEvents.get(portEvent.getDpid());
817 if (linkEvents != null) {
818 for (LinkEvent linkEvent : linkEvents.values()) {
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700819 if (linkEvent.getDst().equals(portEvent.getSwitchPort())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700820 publishRemoveLinkEvent(linkEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700821 }
822 }
823 }
824
825 // Cleanup for the connected hosts
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700826 ConcurrentMap<ByteBuffer, HostEvent> hostEvents =
827 publishedHostEvents.get(portEvent.getDpid());
828 if (hostEvents != null) {
829 for (HostEvent hostEvent : hostEvents.values()) {
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700830 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
831 if (swp.equals(portEvent.getSwitchPort())) {
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700832 publishRemoveHostEvent(hostEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700833 }
834 }
835 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700836 }
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700837
838 portEvents.remove(portEvent.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700839 }
840
841 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700842 * Publishes ADD Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700843 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700844 * @param linkEvent the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700845 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700846 private void publishAddLinkEvent(LinkEvent linkEvent) {
847 if (!registryService.hasControl(linkEvent.getOriginDpid().value())) {
848 log.debug("Not the master for dst switch {}. Suppressed link add event {}.",
849 linkEvent.getOriginDpid(), linkEvent);
850 return;
851 }
852
853 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700854 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700855 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700856 new TopologyEvent(linkEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700857 tbo.appendAddOperation(topologyEvent);
858 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700859
860 // Store the new Link Event in the local cache
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700861 ConcurrentMap<ByteBuffer, LinkEvent> linkEvents =
862 ConcurrentUtils.putIfAbsent(publishedLinkEvents,
863 linkEvent.getDst().getDpid(),
864 new ConcurrentHashMap<ByteBuffer, LinkEvent>());
865 linkEvents.put(linkEvent.getIDasByteBuffer(), linkEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700866 }
867
868 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700869 * Publishes REMOVE Link Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700870 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700871 * @param linkEvent the link event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700872 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700873 private void publishRemoveLinkEvent(LinkEvent linkEvent) {
874 ConcurrentMap<ByteBuffer, LinkEvent> linkEvents =
875 publishedLinkEvents.get(linkEvent.getDst().getDpid());
876 if (linkEvents == null) {
877 return; // Nothing to do
878 }
879 if (linkEvents.get(linkEvent.getIDasByteBuffer()) == null) {
880 return; // Nothing to do
881 }
882
883 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700884 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700885 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700886 new TopologyEvent(linkEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700887 tbo.appendRemoveOperation(topologyEvent);
888 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700889
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700890 linkEvents.remove(linkEvent.getIDasByteBuffer());
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700891 }
892
893 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700894 * Publishes ADD Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700895 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700896 * @param hostEvent the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700897 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700898 private void publishAddHostEvent(HostEvent hostEvent) {
899 //
900 // NOTE: The implementation below assumes that there is just one
901 // attachment point stored in hostEvent. Currently, this assumption
902 // is true based on the existing implementation of the caller
903 // hostAdded().
904 //
905
906 if (!registryService.hasControl(hostEvent.getOriginDpid().value())) {
907 log.debug("Not the master for attachment switch {}. Suppressed host add event {}.",
908 hostEvent.getOriginDpid(), hostEvent);
909 return;
910 }
911
912 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700913 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700914 TopologyEvent topologyEvent =
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700915 new TopologyEvent(hostEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700916 tbo.appendAddOperation(topologyEvent);
917 publishTopologyOperations(tbo);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700918
919 // Store the new Host Event in the local cache
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700920 ConcurrentMap<ByteBuffer, HostEvent> hostEvents =
921 ConcurrentUtils.putIfAbsent(publishedHostEvents,
922 hostEvent.getOriginDpid(),
923 new ConcurrentHashMap<ByteBuffer, HostEvent>());
924 hostEvents.put(hostEvent.getIDasByteBuffer(), hostEvent);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700925 }
926
927 /**
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700928 * Publishes REMOVE Host Event.
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700929 *
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700930 * @param hostEvent the host event to publish
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700931 */
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700932 private void publishRemoveHostEvent(HostEvent hostEvent) {
933 ConcurrentMap<ByteBuffer, HostEvent> hostEvents =
934 publishedHostEvents.get(hostEvent.getOriginDpid());
935 if (hostEvents == null) {
936 return; // Nothing to do
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700937 }
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700938 if (hostEvents.get(hostEvent.getIDasByteBuffer()) == null) {
939 return; // Nothing to do
940 }
941
942 // Publish the information
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700943 TopologyBatchOperation tbo = new TopologyBatchOperation();
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700944 TopologyEvent topologyEvent =
945 new TopologyEvent(hostEvent, getOnosInstanceId());
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700946 tbo.appendRemoveOperation(topologyEvent);
947 publishTopologyOperations(tbo);
Pavlin Radoslavov000fbae2014-08-19 19:42:58 -0700948
949 hostEvents.remove(hostEvent.getIDasByteBuffer());
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700950 }
Pavlin Radoslavov24409672014-08-20 16:45:11 -0700951
952 /**
953 * Publishes Topology Operations.
954 *
955 * @param tbo the Topology Operations to publish
956 */
957 private void publishTopologyOperations(TopologyBatchOperation tbo) {
958 // TODO: This flag should be configurable
959 boolean isGlobalLogWriter = false;
960
961 log.debug("Publishing: {}", tbo);
962
963 if (isGlobalLogWriter) {
964 if (!topologyService.publish(tbo)) {
965 log.debug("Cannot publish: {}", tbo);
966 delayedOperations.add(tbo);
967 }
968 } else {
969 // TODO: For now we publish each TopologyEvent independently
970 for (BatchOperationEntry<TopologyBatchOperation.Operator,
971 TopologyEvent> boe : tbo.getOperations()) {
972 TopologyBatchOperation.Operator oper = boe.getOperator();
973 TopologyEvent topologyEvent = boe.getTarget();
974 switch (oper) {
975 case ADD:
976 eventChannel.addEntry(topologyEvent.getID(),
977 topologyEvent);
978 break;
979 case REMOVE:
980 eventChannel.removeEntry(topologyEvent.getID());
981 break;
982 default:
983 log.error("Unknown Topology Batch Operation {}", oper);
984 break;
985 }
986 }
987 }
988 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800989}