blob: 56ca8fc9f1d0b93392044cc9fb8b1e4269c2b0d6 [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 Radoslavovb46f89b2014-08-15 23:00:26 -07006import java.util.HashMap;
7import 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;
Jonathan Hart369875b2014-02-13 10:00:31 -080010import java.util.concurrent.TimeUnit;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080011
12import net.floodlightcontroller.core.IFloodlightProviderService;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070013import net.floodlightcontroller.core.IFloodlightProviderService.Role;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080014import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070015import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
16import net.floodlightcontroller.core.IOFSwitchListener;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080017import net.floodlightcontroller.core.module.FloodlightModuleContext;
18import net.floodlightcontroller.core.module.FloodlightModuleException;
19import net.floodlightcontroller.core.module.IFloodlightModule;
20import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hart369875b2014-02-13 10:00:31 -080021import net.floodlightcontroller.core.util.SingletonTask;
22import net.floodlightcontroller.threadpool.IThreadPoolService;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070023
24import net.onrc.onos.core.datagrid.IDatagridService;
25import net.onrc.onos.core.datagrid.IEventChannel;
Jonathan Hart03102132014-07-01 23:22:04 -070026import net.onrc.onos.core.hostmanager.Host;
27import net.onrc.onos.core.hostmanager.IHostListener;
28import net.onrc.onos.core.hostmanager.IHostService;
Jonathan Hart23701d12014-04-03 10:45:48 -070029import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryListener;
30import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart284e70f2014-07-05 12:32:51 -070031import net.onrc.onos.core.linkdiscovery.Link;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070032import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070033import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070034import net.onrc.onos.core.registry.RegistryException;
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -070035import net.onrc.onos.core.util.Dpid;
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -070036import net.onrc.onos.core.util.PortNumber;
Yuta HIGUCHI5c8cbeb2014-06-27 11:13:48 -070037import net.onrc.onos.core.util.SwitchPort;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080038
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070039import org.projectfloodlight.openflow.protocol.OFPortDesc;
40import org.projectfloodlight.openflow.util.HexString;
Toshio Koide2f570c12014-02-06 16:55:32 -080041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
Jonathan Hart4b5bbb52014-02-06 10:09:31 -080043
Jonathan Hart88770672014-04-02 18:08:30 -070044/**
Jonathan Harte37e4e22014-05-13 19:12:02 -070045 * The TopologyPublisher subscribes to topology network events from the
46 * discovery modules. These events are reformatted and relayed to the in-memory
47 * topology instance.
Jonathan Hart88770672014-04-02 18:08:30 -070048 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070049public class TopologyPublisher implements IOFSwitchListener,
Ray Milkey269ffb92014-04-03 14:43:30 -070050 ILinkDiscoveryListener,
51 IFloodlightModule,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070052 IHostListener {
Jonathan Hart88770672014-04-02 18:08:30 -070053 private static final Logger log =
Jonathan Harte37e4e22014-05-13 19:12:02 -070054 LoggerFactory.getLogger(TopologyPublisher.class);
Yuta HIGUCHIcb951982014-02-11 13:31:44 -080055
Jonathan Hart88770672014-04-02 18:08:30 -070056 private IFloodlightProviderService floodlightProvider;
57 private ILinkDiscoveryService linkDiscovery;
58 private IControllerRegistryService registryService;
Jonathan Harte37e4e22014-05-13 19:12:02 -070059 private ITopologyService topologyService;
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070060 private IDatagridService datagridService;
Toshio Koide2f570c12014-02-06 16:55:32 -080061
Jonathan Hart03102132014-07-01 23:22:04 -070062 private IHostService hostService;
Jonathan Hartb3e1b052014-04-02 16:01:12 -070063
Jonathan Harte37e4e22014-05-13 19:12:02 -070064 private Topology topology;
Jonathan Hartb3e1b052014-04-02 16:01:12 -070065
Jonathan Hart88770672014-04-02 18:08:30 -070066 private static final String ENABLE_CLEANUP_PROPERTY = "EnableCleanup";
67 private boolean cleanupEnabled = true;
68 private static final int CLEANUP_TASK_INTERVAL = 60; // in seconds
69 private SingletonTask cleanupTask;
Toshio Koide2f570c12014-02-06 16:55:32 -080070
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -070071 private IEventChannel<byte[], TopologyEvent> eventChannel;
72
73 //
74 // Local state for keeping track of locally discovered events so we can
75 // cleanup properly when a Switch or Port is removed.
76 //
77 // We keep all Port, (incoming) Link and Host events per Switch DPID:
78 // - If a switch goes down, we remove all corresponding Port, Link and
79 // Host events.
80 // - If a port on a switch goes down, we remove all corresponding Link
81 // and Host events discovered by this instance.
82 //
83 // How to handle side-effect of remote events.
84 // - Remote Port Down event -> Link Down
85 // Not handled. (XXX Shouldn't it be removed from discovered.. Map)
86 // - Remote Host Added -> lose ownership of Host)
87 // Not handled. (XXX Shouldn't it be removed from discovered.. Map)
88 //
89 // XXX Domain knowledge based invariant maintenance should be moved to
90 // driver module, since the invariant may be different on optical, etc.
91 //
92 // What happens on leadership change?
93 // - Probably should: remove from discovered.. Maps, but not send DELETE
94 // events
95 // XXX Switch/Port can be rediscovered by new leader, but Link, Host?
96 // - Current: There is no way to recognize leadership change?
97 // ZookeeperRegistry.requestControl(long, ControlChangeCallback)
98 // is the only way to register listener, and it allows only one
99 // listener, which is already used by Controller class.
100 //
101 // FIXME Replace with concurrent variant.
102 // #removeSwitchDiscoveryEvent(SwitchEvent) runs in different thread.
103 //
104 private Map<Dpid, Map<ByteBuffer, PortEvent>> discoveredAddedPortEvents =
105 new HashMap<>();
106 private Map<Dpid, Map<ByteBuffer, LinkEvent>> discoveredAddedLinkEvents =
107 new HashMap<>();
108 private Map<Dpid, Map<ByteBuffer, HostEvent>> discoveredAddedHostEvents =
109 new HashMap<>();
110
111
Jonathan Hart369875b2014-02-13 10:00:31 -0800112 /**
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700113 * Cleanup old switches from the topology. Old switches are those which have
114 * no controller in the registry.
Jonathan Hart369875b2014-02-13 10:00:31 -0800115 */
116 private class SwitchCleanup implements ControlChangeCallback, Runnable {
117 @Override
118 public void run() {
119 String old = Thread.currentThread().getName();
120 Thread.currentThread().setName("SwitchCleanup@" + old);
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700121
Jonathan Hart369875b2014-02-13 10:00:31 -0800122 try {
Jonathan Hart88770672014-04-02 18:08:30 -0700123 if (log.isTraceEnabled()) {
124 log.trace("Running cleanup thread");
125 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800126 switchCleanup();
Jonathan Hart369875b2014-02-13 10:00:31 -0800127 } finally {
128 cleanupTask.reschedule(CLEANUP_TASK_INTERVAL,
Jonathan Hart88770672014-04-02 18:08:30 -0700129 TimeUnit.SECONDS);
Jonathan Hart369875b2014-02-13 10:00:31 -0800130 Thread.currentThread().setName(old);
131 }
132 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700133
Jonathan Hart88770672014-04-02 18:08:30 -0700134 /**
135 * First half of the switch cleanup operation. This method will attempt
136 * to get control of any switch it sees without a controller via the
137 * registry.
138 */
Jonathan Hart369875b2014-02-13 10:00:31 -0800139 private void switchCleanup() {
Jonathan Harte37e4e22014-05-13 19:12:02 -0700140 Iterable<Switch> switches = topology.getSwitches();
Jonathan Hart369875b2014-02-13 10:00:31 -0800141
Jonathan Hart88770672014-04-02 18:08:30 -0700142 if (log.isTraceEnabled()) {
143 log.trace("Checking for inactive switches");
144 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700145 // For each switch check if a controller exists in controller
146 // registry
Ray Milkey269ffb92014-04-03 14:43:30 -0700147 for (Switch sw : switches) {
Praseed Balakrishnane82adc62014-08-04 10:59:24 -0700148 // FIXME How to handle case where Switch has never been
149 // registered to ZK
150 if (sw.getConfigState() == ConfigState.CONFIGURED) {
151 continue;
152 }
Jonathan Hart88770672014-04-02 18:08:30 -0700153 try {
154 String controller =
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700155 registryService.getControllerForSwitch(sw.getDpid().value());
Jonathan Hart88770672014-04-02 18:08:30 -0700156 if (controller == null) {
157 log.debug("Requesting control to set switch {} INACTIVE",
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700158 sw.getDpid());
159 registryService.requestControl(sw.getDpid().value(), this);
Jonathan Hart88770672014-04-02 18:08:30 -0700160 }
161 } catch (RegistryException e) {
162 log.error("Caught RegistryException in cleanup thread", e);
163 }
164 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800165 }
166
Jonathan Hart88770672014-04-02 18:08:30 -0700167 /**
168 * Second half of the switch cleanup operation. If the registry grants
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700169 * control of a switch, we can be sure no other instance is writing this
170 * switch to the topology, so we can remove it now.
171 * <p>
172 * @param dpid the dpid of the switch we requested control for
Jonathan Hart88770672014-04-02 18:08:30 -0700173 * @param hasControl whether we got control or not
174 */
175 @Override
176 public void controlChanged(long dpid, boolean hasControl) {
177 if (hasControl) {
178 log.debug("Got control to set switch {} INACTIVE",
179 HexString.toHexString(dpid));
Jonathan Harte02cf542014-04-02 16:24:44 -0700180
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700181 SwitchEvent switchEvent = new SwitchEvent(new Dpid(dpid));
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700182 removeSwitchDiscoveryEvent(switchEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700183 registryService.releaseControl(dpid);
184 }
185 }
Jonathan Hart369875b2014-02-13 10:00:31 -0800186 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800187
Jonathan Hart88770672014-04-02 18:08:30 -0700188 @Override
Jonathan Hart284e70f2014-07-05 12:32:51 -0700189 public void linkAdded(Link link) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700190 LinkEvent linkEvent = new LinkEvent(
Jonathan Hart284e70f2014-07-05 12:32:51 -0700191 new SwitchPort(link.getSrc(), link.getSrcPort()),
192 new SwitchPort(link.getDst(), link.getDstPort()));
193
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700194 // FIXME should be merging, with existing attrs, etc..
195 // TODO define attr name as constant somewhere.
196 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700197 linkEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700198 TopologyElement.TYPE_PACKET_LAYER);
Praseed Balakrishnan2aa6c0b2014-07-17 11:42:05 -0700199 linkEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
200 ConfigState.NOT_CONFIGURED.toString());
201 linkEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
202 AdminStatus.ACTIVE.toString());
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700203 linkEvent.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700204
Jonathan Hart284e70f2014-07-05 12:32:51 -0700205 if (!registryService.hasControl(link.getDst())) {
206 // Don't process or send a link event if we're not master for the
207 // destination switch
208 log.debug("Not the master for dst switch {}. Suppressed link add event {}.",
209 link.getDst(), linkEvent);
210 return;
Jonathan Hart88770672014-04-02 18:08:30 -0700211 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700212 putLinkDiscoveryEvent(linkEvent);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700213 }
214
215 @Override
216 public void linkRemoved(Link link) {
217 LinkEvent linkEvent = new LinkEvent(
218 new SwitchPort(link.getSrc(), link.getSrcPort()),
219 new SwitchPort(link.getDst(), link.getDstPort()));
220
221 // FIXME should be merging, with existing attrs, etc..
222 // TODO define attr name as constant somewhere.
223 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700224 linkEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700225 TopologyElement.TYPE_PACKET_LAYER);
Jonathan Hart284e70f2014-07-05 12:32:51 -0700226 linkEvent.freeze();
227
228 if (!registryService.hasControl(link.getDst())) {
229 // Don't process or send a link event if we're not master for the
230 // destination switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700231 log.debug(
232 "Not the master for dst switch {}. Suppressed link remove event {}.",
Jonathan Hart284e70f2014-07-05 12:32:51 -0700233 link.getDst(), linkEvent);
234 return;
235 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700236 removeLinkDiscoveryEvent(linkEvent);
Jonathan Hart88770672014-04-02 18:08:30 -0700237 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800238
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700239 /* *****************
240 * IOFSwitchListener
241 * *****************/
242
Jonathan Hart88770672014-04-02 18:08:30 -0700243 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700244 public void switchActivatedMaster(long swId) {
245 IOFSwitch sw = floodlightProvider.getSwitch(swId);
246 final Dpid dpid = new Dpid(swId);
247 if (sw == null) {
248 log.warn("Added switch not available {} ", dpid);
249 return;
250 }
251
252 controllerRoleChanged(dpid, Role.MASTER);
253
254 SwitchEvent switchEvent = new SwitchEvent(dpid);
255 // FIXME should be merging, with existing attrs, etc..
256 // TODO define attr name as constant somewhere.
257 // TODO populate appropriate attributes.
258 switchEvent.createStringAttribute(TopologyElement.TYPE,
259 TopologyElement.TYPE_PACKET_LAYER);
260 switchEvent.createStringAttribute("ConnectedSince",
261 sw.getConnectedSince().toString());
262 switchEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
263 ConfigState.NOT_CONFIGURED.toString());
264 switchEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
265 AdminStatus.ACTIVE.toString());
266 switchEvent.freeze();
267 // TODO Not very robust
268 if (!registryService.hasControl(swId)) {
269 log.debug("Not the master for switch {}. Suppressed switch add event {}.",
270 dpid, switchEvent);
271 return;
272 }
273 List<PortEvent> portEvents = new ArrayList<PortEvent>();
274 for (OFPortDesc port : sw.getPorts()) {
275 PortEvent portEvent = new PortEvent(dpid,
276 new PortNumber(port.getPortNo().getShortPortNumber()));
277 // FIXME should be merging, with existing attrs, etc..
278 // TODO define attr name as constant somewhere.
279 // TODO populate appropriate attributes.
280 portEvent.createStringAttribute("name", port.getName());
281 portEvent.createStringAttribute(TopologyElement.TYPE,
282 TopologyElement.TYPE_PACKET_LAYER);
283 portEvent.createStringAttribute(TopologyElement.ELEMENT_CONFIG_STATE,
284 ConfigState.NOT_CONFIGURED.toString());
285 portEvent.createStringAttribute(TopologyElement.ELEMENT_ADMIN_STATUS,
286 AdminStatus.ACTIVE.toString());
287
288 portEvent.freeze();
289 portEvents.add(portEvent);
290 }
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700291 putSwitchDiscoveryEvent(switchEvent, portEvents);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700292
293 for (OFPortDesc port : sw.getPorts()) {
294 // Allow links to be discovered on this port now that it's
295 // in the database
296 linkDiscovery.enableDiscoveryOnPort(sw.getId(),
297 port.getPortNo().getShortPortNumber());
298 }
299 }
300
301 @Override
302 public void switchActivatedEqual(long swId) {
303 final Dpid dpid = new Dpid(swId);
304 controllerRoleChanged(dpid, Role.EQUAL);
305 }
306
307 @Override
308 public void switchMasterToEqual(long swId) {
309 final Dpid dpid = new Dpid(swId);
310 controllerRoleChanged(dpid, Role.EQUAL);
311 }
312
313 @Override
314 public void switchEqualToMaster(long swId) {
315 // for now treat as switchActivatedMaster
316 switchActivatedMaster(swId);
317 }
318
319 @Override
320 public void switchDisconnected(long swId) {
321 final Dpid dpid = new Dpid(swId);
322
323 log.debug("Local switch disconnected: dpid = {} role = {}", dpid);
324
325 Role role = Role.SLAVE; // TODO: Should be Role.UNKNOWN
326
327 MastershipEvent mastershipEvent =
328 new MastershipEvent(dpid, registryService.getOnosInstanceId(),
329 role);
330 // FIXME should be merging, with existing attrs, etc..
331 // TODO define attr name as constant somewhere.
332 // TODO populate appropriate attributes.
333 mastershipEvent.createStringAttribute(TopologyElement.TYPE,
334 TopologyElement.TYPE_ALL_LAYERS);
335 mastershipEvent.freeze();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700336 removeSwitchMastershipEvent(mastershipEvent);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700337 }
338
339 @Override
340 public void switchPortChanged(long swId, OFPortDesc port,
341 PortChangeType changeType) {
342 switch (changeType) {
343 case ADD:
344 switchPortAdded(swId, port);
345 break;
346 case DELETE:
347 switchPortRemoved(swId, port);
348 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700349 case UP:
Pavlin Radoslavov7946c612014-08-13 15:06:00 -0700350 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
351 switchPortAdded(swId, port);
352 break;
353 case DOWN:
354 // NOTE: Currently, we treat Port UP/DOWN same as Port ADD/DELETE
355 switchPortRemoved(swId, port);
356 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700357 case OTHER_UPDATE:
358 default:
359 // XXX S what is the right set of port change handlers?
360 log.debug("Topology publisher does not handle these port updates: {}",
361 changeType);
362 }
363 }
364
365 private void switchPortAdded(long switchId, OFPortDesc port) {
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700366 final Dpid dpid = new Dpid(switchId);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700367 PortEvent portEvent = new PortEvent(dpid,
368 new PortNumber(port.getPortNo().getShortPortNumber()));
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700369 // FIXME should be merging, with existing attrs, etc..
370 // TODO define attr name as constant somewhere.
371 // TODO populate appropriate attributes.
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700372 portEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700373 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700374 portEvent.createStringAttribute("name", port.getName());
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700375
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700376 portEvent.freeze();
377
Jonathan Hart67b6cba2014-05-30 22:36:37 -0700378 if (registryService.hasControl(switchId)) {
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700379 putPortDiscoveryEvent(portEvent);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700380 linkDiscovery.enableDiscoveryOnPort(switchId,
381 port.getPortNo().getShortPortNumber());
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700382 } else {
383 log.debug("Not the master for switch {}. Suppressed port add event {}.",
384 new Dpid(switchId), portEvent);
Jonathan Hart67b6cba2014-05-30 22:36:37 -0700385 }
Jonathan Hart88770672014-04-02 18:08:30 -0700386 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800387
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700388 private void switchPortRemoved(long switchId, OFPortDesc port) {
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700389 final Dpid dpid = new Dpid(switchId);
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700390
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700391 PortEvent portEvent = new PortEvent(dpid, new PortNumber(
392 port.getPortNo().getShortPortNumber()));
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700393 // FIXME should be merging, with existing attrs, etc..
394 // TODO define attr name as constant somewhere.
395 // TODO populate appropriate attributes.
396 portEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700397 TopologyElement.TYPE_PACKET_LAYER);
Yuta HIGUCHI1222ac52014-07-09 16:50:28 -0700398 portEvent.createStringAttribute("name", port.getName());
399
400 portEvent.freeze();
401
Jonathan Hart67b6cba2014-05-30 22:36:37 -0700402 if (registryService.hasControl(switchId)) {
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700403 removePortDiscoveryEvent(portEvent);
Yuta HIGUCHI5bbbaca2014-06-09 16:39:08 -0700404 } else {
405 log.debug("Not the master for switch {}. Suppressed port del event {}.",
Yuta HIGUCHIe2a4e172014-07-03 10:50:39 -0700406 dpid, portEvent);
Jonathan Hart67b6cba2014-05-30 22:36:37 -0700407 }
Jonathan Hart88770672014-04-02 18:08:30 -0700408 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800409
Jonathan Hart88770672014-04-02 18:08:30 -0700410 @Override
Jonathan Hart88770672014-04-02 18:08:30 -0700411 public String getName() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700412 return "topologyPublisher";
Jonathan Hart88770672014-04-02 18:08:30 -0700413 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800414
Jonathan Hart88770672014-04-02 18:08:30 -0700415 /* *****************
416 * IFloodlightModule
417 * *****************/
Toshio Koide2f570c12014-02-06 16:55:32 -0800418
Jonathan Hart88770672014-04-02 18:08:30 -0700419 @Override
420 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
421 return null;
422 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800423
Jonathan Hart88770672014-04-02 18:08:30 -0700424 @Override
425 public Map<Class<? extends IFloodlightService>, IFloodlightService>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700426 getServiceImpls() {
Jonathan Hart88770672014-04-02 18:08:30 -0700427 return null;
428 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800429
Jonathan Hart88770672014-04-02 18:08:30 -0700430 @Override
431 public Collection<Class<? extends IFloodlightService>>
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700432 getModuleDependencies() {
Jonathan Hart88770672014-04-02 18:08:30 -0700433 Collection<Class<? extends IFloodlightService>> l =
434 new ArrayList<Class<? extends IFloodlightService>>();
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800435 l.add(IFloodlightProviderService.class);
436 l.add(ILinkDiscoveryService.class);
Jonathan Hart369875b2014-02-13 10:00:31 -0800437 l.add(IThreadPoolService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800438 l.add(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700439 l.add(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700440 l.add(ITopologyService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700441 l.add(IHostService.class);
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800442 return l;
Jonathan Hart88770672014-04-02 18:08:30 -0700443 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800444
Jonathan Hart88770672014-04-02 18:08:30 -0700445 @Override
446 public void init(FloodlightModuleContext context)
447 throws FloodlightModuleException {
448 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
449 linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class);
450 registryService = context.getServiceImpl(IControllerRegistryService.class);
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700451 datagridService = context.getServiceImpl(IDatagridService.class);
Jonathan Hart03102132014-07-01 23:22:04 -0700452 hostService = context.getServiceImpl(IHostService.class);
Toshio Koide2f570c12014-02-06 16:55:32 -0800453
Jonathan Harte37e4e22014-05-13 19:12:02 -0700454 topologyService = context.getServiceImpl(ITopologyService.class);
Jonathan Hart88770672014-04-02 18:08:30 -0700455 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800456
Jonathan Hart88770672014-04-02 18:08:30 -0700457 @Override
458 public void startUp(FloodlightModuleContext context) {
459 floodlightProvider.addOFSwitchListener(this);
460 linkDiscovery.addListener(this);
Jonathan Hart03102132014-07-01 23:22:04 -0700461 hostService.addHostListener(this);
Toshio Koide2f570c12014-02-06 16:55:32 -0800462
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700463 eventChannel = datagridService.createChannel(
464 TopologyManager.EVENT_CHANNEL_NAME,
465 byte[].class,
466 TopologyEvent.class);
467
Jonathan Harte37e4e22014-05-13 19:12:02 -0700468 topology = topologyService.getTopology();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700469
Jonathan Hart88770672014-04-02 18:08:30 -0700470 // Run the cleanup thread
471 String enableCleanup =
472 context.getConfigParams(this).get(ENABLE_CLEANUP_PROPERTY);
473 if (enableCleanup != null
474 && enableCleanup.equalsIgnoreCase("false")) {
475 cleanupEnabled = false;
476 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700477
Jonathan Hart88770672014-04-02 18:08:30 -0700478 log.debug("Cleanup thread is {}enabled", (cleanupEnabled) ? "" : "not ");
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700479
Jonathan Hart88770672014-04-02 18:08:30 -0700480 if (cleanupEnabled) {
481 IThreadPoolService threadPool =
482 context.getServiceImpl(IThreadPoolService.class);
483 cleanupTask = new SingletonTask(threadPool.getScheduledExecutor(),
484 new SwitchCleanup());
485 // Run the cleanup task immediately on startup
486 cleanupTask.reschedule(0, TimeUnit.SECONDS);
487 }
488 }
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700489
Jonathan Hart88770672014-04-02 18:08:30 -0700490 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700491 public void hostAdded(Host host) {
492 log.debug("Called onosDeviceAdded mac {}", host.getMacAddress());
TeruUd1c5b652014-03-24 13:58:46 -0700493
Jonathan Hart03102132014-07-01 23:22:04 -0700494 SwitchPort sp = new SwitchPort(host.getSwitchDPID(), host.getSwitchPort());
Jonathan Hart88770672014-04-02 18:08:30 -0700495 List<SwitchPort> spLists = new ArrayList<SwitchPort>();
496 spLists.add(sp);
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700497 HostEvent event = new HostEvent(host.getMacAddress());
Jonathan Hart88770672014-04-02 18:08:30 -0700498 event.setAttachmentPoints(spLists);
Jonathan Hart03102132014-07-01 23:22:04 -0700499 event.setLastSeenTime(host.getLastSeenTimestamp().getTime());
Jonathan Hart88770672014-04-02 18:08:30 -0700500 // Does not use vlan info now.
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700501 event.freeze();
Jonathan Hartb3e1b052014-04-02 16:01:12 -0700502
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700503 putHostDiscoveryEvent(event);
Jonathan Hart88770672014-04-02 18:08:30 -0700504 }
TeruUd1c5b652014-03-24 13:58:46 -0700505
Jonathan Hart88770672014-04-02 18:08:30 -0700506 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700507 public void hostRemoved(Host host) {
Jonathan Hart88770672014-04-02 18:08:30 -0700508 log.debug("Called onosDeviceRemoved");
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700509 HostEvent event = new HostEvent(host.getMacAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700510 // XXX shouldn't we be setting attachment points?
Yuta HIGUCHIbf0a8712014-06-30 18:59:46 -0700511 event.freeze();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700512 removeHostDiscoveryEvent(event);
Jonathan Hart88770672014-04-02 18:08:30 -0700513 }
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700514
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700515 private void controllerRoleChanged(Dpid dpid, Role role) {
516 log.debug("Local switch controller mastership role changed: dpid = {} role = {}",
517 dpid, role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700518 MastershipEvent mastershipEvent =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700519 new MastershipEvent(dpid, registryService.getOnosInstanceId(),
520 role);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700521 // FIXME should be merging, with existing attrs, etc..
522 // TODO define attr name as constant somewhere.
523 // TODO populate appropriate attributes.
524 mastershipEvent.createStringAttribute(TopologyElement.TYPE,
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700525 TopologyElement.TYPE_ALL_LAYERS);
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700526 mastershipEvent.freeze();
Pavlin Radoslavovb46f89b2014-08-15 23:00:26 -0700527 putSwitchMastershipEvent(mastershipEvent);
528 }
529
530 /**
531 * Mastership updated event.
532 *
533 * @param mastershipEvent the mastership event.
534 */
535 private void putSwitchMastershipEvent(MastershipEvent mastershipEvent) {
536 // Send out notification
537 TopologyEvent topologyEvent =
538 new TopologyEvent(mastershipEvent,
539 registryService.getOnosInstanceId());
540 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
541 }
542
543 /**
544 * Mastership removed event.
545 *
546 * @param mastershipEvent the mastership event.
547 */
548 private void removeSwitchMastershipEvent(MastershipEvent mastershipEvent) {
549 // Send out notification
550 TopologyEvent topologyEvent =
551 new TopologyEvent(mastershipEvent,
552 registryService.getOnosInstanceId());
553 eventChannel.removeEntry(topologyEvent.getID());
554 }
555
556 /**
557 * Switch discovered event.
558 *
559 * @param switchEvent the switch event.
560 * @param portEvents the corresponding port events for the switch.
561 */
562 private void putSwitchDiscoveryEvent(SwitchEvent switchEvent,
563 Collection<PortEvent> portEvents) {
564 log.debug("Sending add switch: {}", switchEvent);
565 // Send out notification
566 TopologyEvent topologyEvent =
567 new TopologyEvent(switchEvent,
568 registryService.getOnosInstanceId());
569 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
570
571 // Send out notification for each port
572 for (PortEvent portEvent : portEvents) {
573 log.debug("Sending add port: {}", portEvent);
574 topologyEvent =
575 new TopologyEvent(portEvent,
576 registryService.getOnosInstanceId());
577 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
578 }
579
580 //
581 // Keep track of the added ports
582 //
583 // Get the old Port Events
584 Map<ByteBuffer, PortEvent> oldPortEvents =
585 discoveredAddedPortEvents.get(switchEvent.getDpid());
586 if (oldPortEvents == null) {
587 oldPortEvents = new HashMap<>();
588 }
589
590 // Store the new Port Events in the local cache
591 Map<ByteBuffer, PortEvent> newPortEvents = new HashMap<>();
592 for (PortEvent portEvent : portEvents) {
593 ByteBuffer id = portEvent.getIDasByteBuffer();
594 newPortEvents.put(id, portEvent);
595 }
596 discoveredAddedPortEvents.put(switchEvent.getDpid(), newPortEvents);
597
598 //
599 // Extract the removed ports
600 //
601 List<PortEvent> removedPortEvents = new LinkedList<>();
602 for (Map.Entry<ByteBuffer, PortEvent> entry : oldPortEvents.entrySet()) {
603 ByteBuffer key = entry.getKey();
604 PortEvent portEvent = entry.getValue();
605 if (!newPortEvents.containsKey(key)) {
606 removedPortEvents.add(portEvent);
607 }
608 }
609
610 // Cleanup old removed ports
611 for (PortEvent portEvent : removedPortEvents) {
612 removePortDiscoveryEvent(portEvent);
613 }
614 }
615
616 /**
617 * Switch removed event.
618 *
619 * @param switchEvent the switch event.
620 */
621 private void removeSwitchDiscoveryEvent(SwitchEvent switchEvent) {
622 TopologyEvent topologyEvent;
623
624 // Get the old Port Events
625 Map<ByteBuffer, PortEvent> oldPortEvents =
626 discoveredAddedPortEvents.get(switchEvent.getDpid());
627 if (oldPortEvents == null) {
628 oldPortEvents = new HashMap<>();
629 }
630
631 log.debug("Sending remove switch: {}", switchEvent);
632 // Send out notification
633 topologyEvent =
634 new TopologyEvent(switchEvent,
635 registryService.getOnosInstanceId());
636 eventChannel.removeEntry(topologyEvent.getID());
637
638 //
639 // Send out notification for each port.
640 //
641 // NOTE: We don't use removePortDiscoveryEvent() for the cleanup,
642 // because it will attempt to remove the port from the database,
643 // and the deactiveSwitch() call above already removed all ports.
644 //
645 for (PortEvent portEvent : oldPortEvents.values()) {
646 log.debug("Sending remove port:", portEvent);
647 topologyEvent =
648 new TopologyEvent(portEvent,
649 registryService.getOnosInstanceId());
650 eventChannel.removeEntry(topologyEvent.getID());
651 }
652 discoveredAddedPortEvents.remove(switchEvent.getDpid());
653
654 // Cleanup for each link
655 Map<ByteBuffer, LinkEvent> oldLinkEvents =
656 discoveredAddedLinkEvents.get(switchEvent.getDpid());
657 if (oldLinkEvents != null) {
658 for (LinkEvent linkEvent : new ArrayList<>(oldLinkEvents.values())) {
659 removeLinkDiscoveryEvent(linkEvent);
660 }
661 discoveredAddedLinkEvents.remove(switchEvent.getDpid());
662 }
663
664 // Cleanup for each host
665 Map<ByteBuffer, HostEvent> oldHostEvents =
666 discoveredAddedHostEvents.get(switchEvent.getDpid());
667 if (oldHostEvents != null) {
668 for (HostEvent hostEvent : new ArrayList<>(oldHostEvents.values())) {
669 removeHostDiscoveryEvent(hostEvent);
670 }
671 discoveredAddedHostEvents.remove(switchEvent.getDpid());
672 }
673 }
674
675 /**
676 * Port discovered event.
677 *
678 * @param portEvent the port event.
679 */
680 private void putPortDiscoveryEvent(PortEvent portEvent) {
681 log.debug("Sending add port: {}", portEvent);
682 // Send out notification
683 TopologyEvent topologyEvent =
684 new TopologyEvent(portEvent,
685 registryService.getOnosInstanceId());
686 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
687
688 // Store the new Port Event in the local cache
689 Map<ByteBuffer, PortEvent> oldPortEvents =
690 discoveredAddedPortEvents.get(portEvent.getDpid());
691 if (oldPortEvents == null) {
692 oldPortEvents = new HashMap<>();
693 discoveredAddedPortEvents.put(portEvent.getDpid(), oldPortEvents);
694 }
695 ByteBuffer id = portEvent.getIDasByteBuffer();
696 oldPortEvents.put(id, portEvent);
697 }
698
699 /**
700 * Port removed event.
701 *
702 * @param portEvent the port event.
703 */
704 private void removePortDiscoveryEvent(PortEvent portEvent) {
705 log.debug("Sending remove port: {}", portEvent);
706 // Send out notification
707 TopologyEvent topologyEvent =
708 new TopologyEvent(portEvent,
709 registryService.getOnosInstanceId());
710 eventChannel.removeEntry(topologyEvent.getID());
711
712 // Cleanup the Port Event from the local cache
713 Map<ByteBuffer, PortEvent> oldPortEvents =
714 discoveredAddedPortEvents.get(portEvent.getDpid());
715 if (oldPortEvents != null) {
716 ByteBuffer id = portEvent.getIDasByteBuffer();
717 oldPortEvents.remove(id);
718 }
719
720 // Cleanup for the incoming link
721 Map<ByteBuffer, LinkEvent> oldLinkEvents =
722 discoveredAddedLinkEvents.get(portEvent.getDpid());
723 if (oldLinkEvents != null) {
724 for (LinkEvent linkEvent : new ArrayList<>(oldLinkEvents.values())) {
725 if (linkEvent.getDst().equals(portEvent.getSwitchPort())) {
726 removeLinkDiscoveryEvent(linkEvent);
727 // XXX If we change our model to allow multiple Link on
728 // a Port, this loop must be fixed to allow continuing.
729 break;
730 }
731 }
732 }
733
734 // Cleanup for the connected hosts
735 // TODO: The implementation below is probably wrong
736 List<HostEvent> removedHostEvents = new LinkedList<>();
737 Map<ByteBuffer, HostEvent> oldHostEvents =
738 discoveredAddedHostEvents.get(portEvent.getDpid());
739 if (oldHostEvents != null) {
740 for (HostEvent hostEvent : new ArrayList<>(oldHostEvents.values())) {
741 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
742 if (swp.equals(portEvent.getSwitchPort())) {
743 removedHostEvents.add(hostEvent);
744 }
745 }
746 }
747 for (HostEvent hostEvent : removedHostEvents) {
748 removeHostDiscoveryEvent(hostEvent);
749 }
750 }
751 }
752
753 /**
754 * Link discovered event.
755 *
756 * @param linkEvent the link event.
757 */
758 private void putLinkDiscoveryEvent(LinkEvent linkEvent) {
759 log.debug("Sending add link: {}", linkEvent);
760 // Send out notification
761 TopologyEvent topologyEvent =
762 new TopologyEvent(linkEvent,
763 registryService.getOnosInstanceId());
764 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
765
766 // Store the new Link Event in the local cache
767 Map<ByteBuffer, LinkEvent> oldLinkEvents =
768 discoveredAddedLinkEvents.get(linkEvent.getDst().getDpid());
769 if (oldLinkEvents == null) {
770 oldLinkEvents = new HashMap<>();
771 discoveredAddedLinkEvents.put(linkEvent.getDst().getDpid(),
772 oldLinkEvents);
773 }
774 ByteBuffer id = linkEvent.getIDasByteBuffer();
775 oldLinkEvents.put(id, linkEvent);
776 }
777
778 /**
779 * Link removed event.
780 *
781 * @param linkEvent the link event.
782 */
783 private void removeLinkDiscoveryEvent(LinkEvent linkEvent) {
784 log.debug("Sending remove link: {}", linkEvent);
785 // Send out notification
786 TopologyEvent topologyEvent =
787 new TopologyEvent(linkEvent,
788 registryService.getOnosInstanceId());
789 eventChannel.removeEntry(topologyEvent.getID());
790
791 // Cleanup the Link Event from the local cache
792 Map<ByteBuffer, LinkEvent> oldLinkEvents =
793 discoveredAddedLinkEvents.get(linkEvent.getDst().getDpid());
794 if (oldLinkEvents != null) {
795 ByteBuffer id = linkEvent.getIDasByteBuffer();
796 oldLinkEvents.remove(id);
797 }
798 }
799
800 /**
801 * Host discovered event.
802 *
803 * @param hostEvent the host event.
804 */
805 private void putHostDiscoveryEvent(HostEvent hostEvent) {
806 // Send out notification
807 TopologyEvent topologyEvent =
808 new TopologyEvent(hostEvent,
809 registryService.getOnosInstanceId());
810 eventChannel.addEntry(topologyEvent.getID(), topologyEvent);
811 log.debug("Put the host info into the cache of the topology. mac {}",
812 hostEvent.getMac());
813
814 // Store the new Host Event in the local cache
815 // TODO: The implementation below is probably wrong
816 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
817 Map<ByteBuffer, HostEvent> oldHostEvents =
818 discoveredAddedHostEvents.get(swp.getDpid());
819 if (oldHostEvents == null) {
820 oldHostEvents = new HashMap<>();
821 discoveredAddedHostEvents.put(swp.getDpid(), oldHostEvents);
822 }
823 ByteBuffer id = hostEvent.getIDasByteBuffer();
824 oldHostEvents.put(id, hostEvent);
825 }
826 }
827
828 /**
829 * Host removed event.
830 *
831 * @param hostEvent the host event.
832 */
833 private void removeHostDiscoveryEvent(HostEvent hostEvent) {
834 // Send out notification
835 TopologyEvent topologyEvent =
836 new TopologyEvent(hostEvent,
837 registryService.getOnosInstanceId());
838 eventChannel.removeEntry(topologyEvent.getID());
839 log.debug("Remove the host info into the cache of the topology. mac {}",
840 hostEvent.getMac());
841
842 // Cleanup the Host Event from the local cache
843 // TODO: The implementation below is probably wrong
844 ByteBuffer id = hostEvent.getIDasByteBuffer();
845 for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
846 Map<ByteBuffer, HostEvent> oldHostEvents =
847 discoveredAddedHostEvents.get(swp.getDpid());
848 if (oldHostEvents != null) {
849 oldHostEvents.remove(id);
850 }
851 }
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700852 }
Jonathan Hart4b5bbb52014-02-06 10:09:31 -0800853}