blob: 0c7a75e6e5e4be3bb915a40f59ec2ea3f10f132a [file] [log] [blame]
Jonathan Hart23701d12014-04-03 10:45:48 -07001package net.onrc.onos.core.devicemanager;
Jonathan Hartd857ad62013-12-14 18:08:17 -08002
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.Date;
6import java.util.HashMap;
TeruU80ce5062014-03-03 17:16:13 -08007import java.util.HashSet;
Jonathan Hartd857ad62013-12-14 18:08:17 -08008import java.util.List;
9import java.util.Map;
TeruU80ce5062014-03-03 17:16:13 -080010import java.util.Set;
TeruUd1c5b652014-03-24 13:58:46 -070011import java.util.concurrent.CopyOnWriteArrayList;
TeruU80ce5062014-03-03 17:16:13 -080012import java.util.concurrent.Executors;
13import java.util.concurrent.ScheduledExecutorService;
14import java.util.concurrent.TimeUnit;
Jonathan Hartd857ad62013-12-14 18:08:17 -080015
16import net.floodlightcontroller.core.FloodlightContext;
17import net.floodlightcontroller.core.IFloodlightProviderService;
18import net.floodlightcontroller.core.IOFMessageListener;
19import net.floodlightcontroller.core.IOFSwitch;
20import net.floodlightcontroller.core.IUpdate;
21import net.floodlightcontroller.core.module.FloodlightModuleContext;
22import net.floodlightcontroller.core.module.FloodlightModuleException;
23import net.floodlightcontroller.core.module.IFloodlightModule;
24import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hartd857ad62013-12-14 18:08:17 -080025import net.floodlightcontroller.util.MACAddress;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070026import net.onrc.onos.core.packet.Ethernet;
TeruU5d2c9392014-06-09 20:02:02 -070027import net.onrc.onos.core.topology.Device;
Jonathan Harte37e4e22014-05-13 19:12:02 -070028import net.onrc.onos.core.topology.ITopologyService;
TeruU5d2c9392014-06-09 20:02:02 -070029import net.onrc.onos.core.topology.Port;
Jonathan Harte37e4e22014-05-13 19:12:02 -070030import net.onrc.onos.core.topology.Topology;
Jonathan Hartd857ad62013-12-14 18:08:17 -080031
32import org.openflow.protocol.OFMessage;
33import org.openflow.protocol.OFPacketIn;
34import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080035import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080037
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070038public class OnosDeviceManager implements IFloodlightModule,
Ray Milkey269ffb92014-04-03 14:43:30 -070039 IOFMessageListener,
TeruU5d2c9392014-06-09 20:02:02 -070040 IOnosDeviceService {
TeruU28adcc32014-04-15 17:57:35 -070041
Ray Milkeyec838942014-04-09 11:28:43 -070042 private static final Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
TeruU28adcc32014-04-15 17:57:35 -070043 private static final long DEVICE_CLEANING_INITIAL_DELAY = 30;
44 private int cleanupSecondConfig = 60 * 60;
45 private int agingMillisecConfig = 60 * 60 * 1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070046
Ray Milkey269ffb92014-04-03 14:43:30 -070047 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
48 private IFloodlightProviderService floodlightProvider;
Jonathan Hart83ce8c42014-06-02 00:07:06 -070049 private static final ScheduledExecutorService EXECUTOR_SERVICE =
50 Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070051
Jonathan Harte37e4e22014-05-13 19:12:02 -070052 private ITopologyService topologyService;
53 private Topology topology;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070054
TeruU80ce5062014-03-03 17:16:13 -080055 public enum OnosDeviceUpdateType {
56 ADD, DELETE, UPDATE;
57 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070058
Ray Milkey269ffb92014-04-03 14:43:30 -070059 private class OnosDeviceUpdate implements IUpdate {
Patrick Liuab1e6062014-05-05 11:12:13 -070060 private final OnosDevice device;
61 private final OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070062
Ray Milkey269ffb92014-04-03 14:43:30 -070063 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
64 this.device = device;
65 this.type = type;
66 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070067
Ray Milkey269ffb92014-04-03 14:43:30 -070068 @Override
69 public void dispatch() {
70 if (type == OnosDeviceUpdateType.ADD) {
71 for (IOnosDeviceListener listener : deviceListeners) {
72 listener.onosDeviceAdded(device);
73 }
74 } else if (type == OnosDeviceUpdateType.DELETE) {
75 for (IOnosDeviceListener listener : deviceListeners) {
76 listener.onosDeviceRemoved(device);
77 }
78 }
79 }
80 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070081
Ray Milkey269ffb92014-04-03 14:43:30 -070082 @Override
83 public String getName() {
84 return "onosdevicemanager";
85 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080086
Ray Milkey269ffb92014-04-03 14:43:30 -070087 @Override
88 public boolean isCallbackOrderingPrereq(OFType type, String name) {
89 // We want link discovery to consume LLDP first otherwise we'll
90 // end up reading bad device info from LLDP packets
91 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
92 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080093
Ray Milkey269ffb92014-04-03 14:43:30 -070094 @Override
95 public boolean isCallbackOrderingPostreq(OFType type, String name) {
96 return type == OFType.PACKET_IN &&
97 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
98 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080099
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 @Override
101 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700102 if (msg.getType().equals(OFType.PACKET_IN) &&
Patrick Liuab1e6062014-05-05 11:12:13 -0700103 (msg instanceof OFPacketIn)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700104 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700105
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 Ethernet eth = IFloodlightProviderService.bcStore.
107 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700108
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 return processPacketIn(sw, pi, eth);
110 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700111
Ray Milkey269ffb92014-04-03 14:43:30 -0700112 return Command.CONTINUE;
113 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700114
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700115 // This "protected" modifier is for unit test.
116 // The above "receive" method couldn't be tested
117 // because of IFloodlightProviderService static final field.
TeruU28adcc32014-04-15 17:57:35 -0700118 protected Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU5d2c9392014-06-09 20:02:02 -0700119 if (log.isTraceEnabled()) {
120 log.trace("Receive PACKET_IN swId {}, portId {}", sw.getId(), pi.getInPort());
121 }
122
Ray Milkey269ffb92014-04-03 14:43:30 -0700123 long dpid = sw.getId();
TeruU80ce5062014-03-03 17:16:13 -0800124 short portId = pi.getInPort();
125 Long mac = eth.getSourceMAC().toLong();
126
Jonathan Hartd857ad62013-12-14 18:08:17 -0800127 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800128 getSourceDeviceFromPacket(eth, dpid, portId);
129
Ray Milkey269ffb92014-04-03 14:43:30 -0700130 if (srcDevice == null) {
131 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800132 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700133
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700134 // If the switch port we try to attach a new device already has a link,
135 // then don't add the device
136 // TODO We probably don't need to check this here, it should be done in
137 // the Topology module.
TeruU5d2c9392014-06-09 20:02:02 -0700138 topology.acquireReadLock();
139 try {
140 if (topology.getOutgoingLink(dpid, (long) portId) != null ||
141 topology.getIncomingLink(dpid, (long) portId) != null) {
142 log.debug("Stop adding OnosDevice {} as " +
143 "there is a link on the port: dpid {} port {}",
144 srcDevice.getMacAddress(), dpid, portId);
145 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700146 }
TeruU5d2c9392014-06-09 20:02:02 -0700147 } finally {
148 topology.releaseReadLock();
TeruU80ce5062014-03-03 17:16:13 -0800149 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700150
TeruU80ce5062014-03-03 17:16:13 -0800151 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700152
Ray Milkey269ffb92014-04-03 14:43:30 -0700153 if (log.isTraceEnabled()) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700154 log.trace("Add device info: {}", srcDevice);
TeruU80ce5062014-03-03 17:16:13 -0800155 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800156 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700157 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700158
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700159 // Thread to delete devices periodically.
160 // Remove all devices from the map first and then finally delete devices
161 // from the DB.
162
163 // TODO This should be sharded based on device 'owner' (i.e. the instance
164 // that owns the switch it is attached to). Currently any instance can
165 // issue deletes for any device, which permits race conditions and could
166 // cause the Topology replicas to diverge.
Ray Milkey269ffb92014-04-03 14:43:30 -0700167 private class CleanDevice implements Runnable {
168 @Override
169 public void run() {
170 log.debug("called CleanDevice");
TeruU5d2c9392014-06-09 20:02:02 -0700171 topology.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700172 try {
TeruU5d2c9392014-06-09 20:02:02 -0700173 Set<Device> deleteSet = new HashSet<Device>();
174 for (Device dev : topology.getDevices()) {
175 long now = System.currentTimeMillis();
176 if ((now - dev.getLastSeenTime() > agingMillisecConfig)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700177 if (log.isTraceEnabled()) {
TeruU5d2c9392014-06-09 20:02:02 -0700178 log.trace("Removing device info: mac {}, now {}, lastSeenTime {}, diff {}",
179 dev.getMacAddress(), now, dev.getLastSeenTime(), now - dev.getLastSeenTime());
Ray Milkey269ffb92014-04-03 14:43:30 -0700180 }
181 deleteSet.add(dev);
182 }
183 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700184
TeruU5d2c9392014-06-09 20:02:02 -0700185 for (Device dev : deleteSet) {
186 deleteOnosDeviceByMac(dev.getMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -0700187 }
188 } catch (Exception e) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700189 // Any exception thrown by the task will prevent the Executor
190 // from running the next iteration, so we need to catch and log
191 // all exceptions here.
192 log.error("Exception in device cleanup thread:", e);
TeruU5d2c9392014-06-09 20:02:02 -0700193 } finally {
194 topology.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700195 }
196 }
197 }
TeruU80ce5062014-03-03 17:16:13 -0800198
Jonathan Hartd857ad62013-12-14 18:08:17 -0800199 /**
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700200 * Parse a device from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800202 * @param eth the packet to parse
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700203 * @param swdpid the switch on which the packet arrived
204 * @param port the port on which the packet arrived
205 * @return the device from the packet
Jonathan Hartd857ad62013-12-14 18:08:17 -0800206 */
Patrick Liuab1e6062014-05-05 11:12:13 -0700207 protected OnosDevice getSourceDeviceFromPacket(Ethernet eth,
208 long swdpid,
TeruU5d2c9392014-06-09 20:02:02 -0700209 long port) {
Jonathan Hart7ab71612014-05-27 13:37:31 -0700210 MACAddress sourceMac = eth.getSourceMAC();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800211
Jonathan Hart7ab71612014-05-27 13:37:31 -0700212 // Ignore broadcast/multicast source
213 if (sourceMac.isBroadcast() || sourceMac.isBroadcast()) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800214 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700215 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800216
217 short vlan = eth.getVlanID();
Jonathan Hart7ab71612014-05-27 13:37:31 -0700218 return new OnosDevice(sourceMac,
Ray Milkey269ffb92014-04-03 14:43:30 -0700219 ((vlan >= 0) ? vlan : null),
Ray Milkey269ffb92014-04-03 14:43:30 -0700220 swdpid,
221 port,
222 new Date());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800223 }
224
Ray Milkey269ffb92014-04-03 14:43:30 -0700225 @Override
226 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
227 List<Class<? extends IFloodlightService>> services =
228 new ArrayList<Class<? extends IFloodlightService>>();
229 services.add(IOnosDeviceService.class);
230 return services;
231 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800232
TeruUd1c5b652014-03-24 13:58:46 -0700233 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700234 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
235 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
236 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
237 impls.put(IOnosDeviceService.class, this);
238 return impls;
239 }
240
TeruUd1c5b652014-03-24 13:58:46 -0700241 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
243 List<Class<? extends IFloodlightService>> dependencies =
244 new ArrayList<Class<? extends IFloodlightService>>();
245 dependencies.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700246 dependencies.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700247 return dependencies;
248 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700249
Ray Milkey269ffb92014-04-03 14:43:30 -0700250 @Override
251 public void init(FloodlightModuleContext context)
252 throws FloodlightModuleException {
253 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700254 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700255 topologyService = context.getServiceImpl(ITopologyService.class);
256 topology = topologyService.getTopology();
TeruU28adcc32014-04-15 17:57:35 -0700257
258 setOnosDeviceManagerProperty(context);
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 }
TeruU80ce5062014-03-03 17:16:13 -0800260
Ray Milkey269ffb92014-04-03 14:43:30 -0700261 @Override
262 public void startUp(FloodlightModuleContext context) {
263 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700264 EXECUTOR_SERVICE.scheduleAtFixedRate(new CleanDevice(),
265 DEVICE_CLEANING_INITIAL_DELAY, cleanupSecondConfig, TimeUnit.SECONDS);
Ray Milkey269ffb92014-04-03 14:43:30 -0700266 }
TeruUd1c5b652014-03-24 13:58:46 -0700267
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 @Override
269 public void deleteOnosDevice(OnosDevice dev) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700270 floodlightProvider.publishUpdate(
271 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
Ray Milkey269ffb92014-04-03 14:43:30 -0700272 }
TeruUd1c5b652014-03-24 13:58:46 -0700273
Ray Milkey269ffb92014-04-03 14:43:30 -0700274 @Override
275 public void deleteOnosDeviceByMac(MACAddress mac) {
TeruU5d2c9392014-06-09 20:02:02 -0700276 OnosDevice deleteDevice = null;
277 topology.acquireReadLock();
278 try {
279 Device dev = topology.getDeviceByMac(mac);
280
281 for (Port switchPort : dev.getAttachmentPoints()) {
282 // We don't handle vlan now and multiple attachment points.
283 deleteDevice = new OnosDevice(dev.getMacAddress(),
284 null,
285 switchPort.getDpid(),
286 switchPort.getNumber(),
287 new Date(dev.getLastSeenTime()));
288 break;
289 }
290 } finally {
291 topology.releaseReadLock();
292 }
293
294 if (deleteDevice != null) {
295 deleteOnosDevice(deleteDevice);
296 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700297 }
298
299 @Override
300 public void addOnosDevice(Long mac, OnosDevice dev) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700301 floodlightProvider.publishUpdate(
302 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
Ray Milkey269ffb92014-04-03 14:43:30 -0700303 }
304
305 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700306 public void addOnosDeviceListener(IOnosDeviceListener listener) {
307 deviceListeners.add(listener);
308 }
309
310 @Override
311 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
312 deviceListeners.remove(listener);
313 }
TeruU28adcc32014-04-15 17:57:35 -0700314
315 private void setOnosDeviceManagerProperty(FloodlightModuleContext context) {
316 Map<String, String> configOptions = context.getConfigParams(this);
317 String cleanupsec = configOptions.get("cleanupsec");
318 String agingmsec = configOptions.get("agingmsec");
319 if (cleanupsec != null) {
320 cleanupSecondConfig = Integer.parseInt(cleanupsec);
321 log.debug("CLEANUP_SECOND is set to {}", cleanupSecondConfig);
322 }
323
324 if (agingmsec != null) {
325 agingMillisecConfig = Integer.parseInt(agingmsec);
326 log.debug("AGEING_MILLSEC is set to {}", agingMillisecConfig);
327 }
328 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800329}