blob: 23944a0864b89d4e0da4a1225576db8a8b19eb2c [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;
11import java.util.concurrent.ConcurrentHashMap;
TeruUd1c5b652014-03-24 13:58:46 -070012import java.util.concurrent.CopyOnWriteArrayList;
TeruU80ce5062014-03-03 17:16:13 -080013import java.util.concurrent.Executors;
14import java.util.concurrent.ScheduledExecutorService;
15import java.util.concurrent.TimeUnit;
Jonathan Hartd857ad62013-12-14 18:08:17 -080016
17import net.floodlightcontroller.core.FloodlightContext;
18import net.floodlightcontroller.core.IFloodlightProviderService;
19import net.floodlightcontroller.core.IOFMessageListener;
20import net.floodlightcontroller.core.IOFSwitch;
21import net.floodlightcontroller.core.IUpdate;
22import net.floodlightcontroller.core.module.FloodlightModuleContext;
23import net.floodlightcontroller.core.module.FloodlightModuleException;
24import net.floodlightcontroller.core.module.IFloodlightModule;
25import net.floodlightcontroller.core.module.IFloodlightService;
Jonathan Hartd857ad62013-12-14 18:08:17 -080026import net.floodlightcontroller.util.MACAddress;
Jonathan Hart6df90172014-04-03 10:13:11 -070027import net.onrc.onos.core.datagrid.IDatagridService;
28import net.onrc.onos.core.datagrid.IEventChannel;
29import net.onrc.onos.core.datagrid.IEventChannelListener;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070030import net.onrc.onos.core.packet.Ethernet;
Jonathan Harte37e4e22014-05-13 19:12:02 -070031import net.onrc.onos.core.topology.ITopologyService;
32import net.onrc.onos.core.topology.Topology;
Jonathan Hartd857ad62013-12-14 18:08:17 -080033
34import org.openflow.protocol.OFMessage;
35import org.openflow.protocol.OFPacketIn;
36import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080037import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080039
Pavlin Radoslavov902fe522014-03-31 10:11:31 -070040public class OnosDeviceManager implements IFloodlightModule,
Ray Milkey269ffb92014-04-03 14:43:30 -070041 IOFMessageListener,
42 IOnosDeviceService,
43 IEventChannelListener<Long, OnosDevice> {
TeruU28adcc32014-04-15 17:57:35 -070044
Ray Milkeyec838942014-04-09 11:28:43 -070045 private static final Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
TeruU28adcc32014-04-15 17:57:35 -070046 private static final long DEVICE_CLEANING_INITIAL_DELAY = 30;
47 private int cleanupSecondConfig = 60 * 60;
48 private int agingMillisecConfig = 60 * 60 * 1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070049
Ray Milkey269ffb92014-04-03 14:43:30 -070050 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
51 private IFloodlightProviderService floodlightProvider;
Jonathan Hart83ce8c42014-06-02 00:07:06 -070052 private static final ScheduledExecutorService EXECUTOR_SERVICE =
53 Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070054
Jonathan Hart83ce8c42014-06-02 00:07:06 -070055 // TODO This infrastructure maintains a global device cache in the
56 // OnosDeviceManager module on each instance (in mapDevice). We want to
57 // remove this eventually - the global cache should be maintained by the
58 // topology layer (which it currently is as well).
Ray Milkey269ffb92014-04-03 14:43:30 -070059 private IDatagridService datagrid;
60 private IEventChannel<Long, OnosDevice> eventChannel;
61 private static final String DEVICE_CHANNEL_NAME = "onos.device";
Jonathan Hart83ce8c42014-06-02 00:07:06 -070062 private final Map<Long, OnosDevice> mapDevice =
63 new ConcurrentHashMap<Long, OnosDevice>();
64
Jonathan Harte37e4e22014-05-13 19:12:02 -070065 private ITopologyService topologyService;
66 private Topology topology;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070067
TeruU80ce5062014-03-03 17:16:13 -080068 public enum OnosDeviceUpdateType {
69 ADD, DELETE, UPDATE;
70 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070071
Ray Milkey269ffb92014-04-03 14:43:30 -070072 private class OnosDeviceUpdate implements IUpdate {
Patrick Liuab1e6062014-05-05 11:12:13 -070073 private final OnosDevice device;
74 private final OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070075
Ray Milkey269ffb92014-04-03 14:43:30 -070076 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
77 this.device = device;
78 this.type = type;
79 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070080
Ray Milkey269ffb92014-04-03 14:43:30 -070081 @Override
82 public void dispatch() {
83 if (type == OnosDeviceUpdateType.ADD) {
84 for (IOnosDeviceListener listener : deviceListeners) {
85 listener.onosDeviceAdded(device);
86 }
87 } else if (type == OnosDeviceUpdateType.DELETE) {
88 for (IOnosDeviceListener listener : deviceListeners) {
89 listener.onosDeviceRemoved(device);
90 }
91 }
92 }
93 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070094
Ray Milkey269ffb92014-04-03 14:43:30 -070095 @Override
96 public String getName() {
97 return "onosdevicemanager";
98 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080099
Ray Milkey269ffb92014-04-03 14:43:30 -0700100 @Override
101 public boolean isCallbackOrderingPrereq(OFType type, String name) {
102 // We want link discovery to consume LLDP first otherwise we'll
103 // end up reading bad device info from LLDP packets
104 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
105 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800106
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 @Override
108 public boolean isCallbackOrderingPostreq(OFType type, String name) {
109 return type == OFType.PACKET_IN &&
110 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
111 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800112
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 @Override
114 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700115 if (msg.getType().equals(OFType.PACKET_IN) &&
Patrick Liuab1e6062014-05-05 11:12:13 -0700116 (msg instanceof OFPacketIn)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700117 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700118
Ray Milkey269ffb92014-04-03 14:43:30 -0700119 Ethernet eth = IFloodlightProviderService.bcStore.
120 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700121
Ray Milkey269ffb92014-04-03 14:43:30 -0700122 return processPacketIn(sw, pi, eth);
123 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700124
Ray Milkey269ffb92014-04-03 14:43:30 -0700125 return Command.CONTINUE;
126 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700127
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700128 // This "protected" modifier is for unit test.
129 // The above "receive" method couldn't be tested
130 // because of IFloodlightProviderService static final field.
TeruU28adcc32014-04-15 17:57:35 -0700131 protected Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 long dpid = sw.getId();
TeruU80ce5062014-03-03 17:16:13 -0800133 short portId = pi.getInPort();
134 Long mac = eth.getSourceMAC().toLong();
135
Jonathan Hartd857ad62013-12-14 18:08:17 -0800136 OnosDevice srcDevice =
TeruU80ce5062014-03-03 17:16:13 -0800137 getSourceDeviceFromPacket(eth, dpid, portId);
138
Ray Milkey269ffb92014-04-03 14:43:30 -0700139 if (srcDevice == null) {
140 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800141 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700142
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700143 // We check if it is the same device in datagrid to suppress the device update
Ray Milkeyb6e0ac82014-04-09 13:21:42 -0700144 OnosDevice exDev = mapDevice.get(mac);
Jonathan Hartac15d932014-06-01 22:59:35 -0700145 if (exDev != null && exDev.equals(srcDevice)) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700146 // There is the same existing device. Update only ActiveSince time.
147 // TODO This doesn't update the timestamp in the Topology module,
148 // only in the local cache in this local driver module.
Jonathan Hartac15d932014-06-01 22:59:35 -0700149 exDev.setLastSeenTimestamp(new Date());
150 if (log.isTraceEnabled()) {
151 log.trace("In the local cache, there is the same device."
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700152 + " Only update last seen time: {}", exDev);
Ray Milkey269ffb92014-04-03 14:43:30 -0700153 }
Jonathan Hartac15d932014-06-01 22:59:35 -0700154 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700156
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700157 // If the switch port we try to attach a new device already has a link,
158 // then don't add the device
159 // TODO We probably don't need to check this here, it should be done in
160 // the Topology module.
Jonathan Harte37e4e22014-05-13 19:12:02 -0700161 if (topology.getOutgoingLink(dpid, (long) portId) != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700162 if (log.isTraceEnabled()) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700163 log.trace("Stop adding OnosDevice {} as " +
164 "there is a link on the port: dpid {} port {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700165 srcDevice.getMacAddress(), dpid, portId);
166 }
167 return Command.CONTINUE;
TeruU80ce5062014-03-03 17:16:13 -0800168 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700169
TeruU80ce5062014-03-03 17:16:13 -0800170 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700171
Ray Milkey269ffb92014-04-03 14:43:30 -0700172 if (log.isTraceEnabled()) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700173 log.trace("Add device info: {}", srcDevice);
TeruU80ce5062014-03-03 17:16:13 -0800174 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800175 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700176 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700177
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700178 // Thread to delete devices periodically.
179 // Remove all devices from the map first and then finally delete devices
180 // from the DB.
181
182 // TODO This should be sharded based on device 'owner' (i.e. the instance
183 // that owns the switch it is attached to). Currently any instance can
184 // issue deletes for any device, which permits race conditions and could
185 // cause the Topology replicas to diverge.
Ray Milkey269ffb92014-04-03 14:43:30 -0700186 private class CleanDevice implements Runnable {
187 @Override
188 public void run() {
189 log.debug("called CleanDevice");
190 try {
191 Set<OnosDevice> deleteSet = new HashSet<OnosDevice>();
192 for (OnosDevice dev : mapDevice.values()) {
193 long now = new Date().getTime();
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700194 if ((now - dev.getLastSeenTimestamp().getTime()
195 > agingMillisecConfig)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 if (log.isTraceEnabled()) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700197 log.debug("Removing device info from the datagrid: {}, diff {}",
198 dev, now - dev.getLastSeenTimestamp().getTime());
Ray Milkey269ffb92014-04-03 14:43:30 -0700199 }
200 deleteSet.add(dev);
201 }
202 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700203
Ray Milkey269ffb92014-04-03 14:43:30 -0700204 for (OnosDevice dev : deleteSet) {
205 deleteOnosDevice(dev);
206 }
207 } catch (Exception e) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700208 // Any exception thrown by the task will prevent the Executor
209 // from running the next iteration, so we need to catch and log
210 // all exceptions here.
211 log.error("Exception in device cleanup thread:", e);
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 }
213 }
214 }
TeruU80ce5062014-03-03 17:16:13 -0800215
Jonathan Hartd857ad62013-12-14 18:08:17 -0800216 /**
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700217 * Parse a device from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700218 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800219 * @param eth the packet to parse
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700220 * @param swdpid the switch on which the packet arrived
221 * @param port the port on which the packet arrived
222 * @return the device from the packet
Jonathan Hartd857ad62013-12-14 18:08:17 -0800223 */
Patrick Liuab1e6062014-05-05 11:12:13 -0700224 protected OnosDevice getSourceDeviceFromPacket(Ethernet eth,
225 long swdpid,
226 short port) {
Jonathan Hart7ab71612014-05-27 13:37:31 -0700227 MACAddress sourceMac = eth.getSourceMAC();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800228
Jonathan Hart7ab71612014-05-27 13:37:31 -0700229 // Ignore broadcast/multicast source
230 if (sourceMac.isBroadcast() || sourceMac.isBroadcast()) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800231 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700232 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800233
234 short vlan = eth.getVlanID();
Jonathan Hart7ab71612014-05-27 13:37:31 -0700235 return new OnosDevice(sourceMac,
Ray Milkey269ffb92014-04-03 14:43:30 -0700236 ((vlan >= 0) ? vlan : null),
Ray Milkey269ffb92014-04-03 14:43:30 -0700237 swdpid,
238 port,
239 new Date());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800240 }
241
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 @Override
243 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
244 List<Class<? extends IFloodlightService>> services =
245 new ArrayList<Class<? extends IFloodlightService>>();
246 services.add(IOnosDeviceService.class);
247 return services;
248 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800249
TeruUd1c5b652014-03-24 13:58:46 -0700250 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700251 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
252 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
253 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
254 impls.put(IOnosDeviceService.class, this);
255 return impls;
256 }
257
TeruUd1c5b652014-03-24 13:58:46 -0700258 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
260 List<Class<? extends IFloodlightService>> dependencies =
261 new ArrayList<Class<? extends IFloodlightService>>();
262 dependencies.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700263 dependencies.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700264 dependencies.add(IDatagridService.class);
265 return dependencies;
266 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700267
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 @Override
269 public void init(FloodlightModuleContext context)
270 throws FloodlightModuleException {
271 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700272 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
273 datagrid = context.getServiceImpl(IDatagridService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700274 topologyService = context.getServiceImpl(ITopologyService.class);
275 topology = topologyService.getTopology();
TeruU28adcc32014-04-15 17:57:35 -0700276
277 setOnosDeviceManagerProperty(context);
Ray Milkey269ffb92014-04-03 14:43:30 -0700278 }
TeruU80ce5062014-03-03 17:16:13 -0800279
Ray Milkey269ffb92014-04-03 14:43:30 -0700280 @Override
281 public void startUp(FloodlightModuleContext context) {
282 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
283 eventChannel = datagrid.addListener(DEVICE_CHANNEL_NAME, this,
284 Long.class,
285 OnosDevice.class);
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700286 EXECUTOR_SERVICE.scheduleAtFixedRate(new CleanDevice(),
287 DEVICE_CLEANING_INITIAL_DELAY, cleanupSecondConfig, TimeUnit.SECONDS);
Ray Milkey269ffb92014-04-03 14:43:30 -0700288 }
TeruUd1c5b652014-03-24 13:58:46 -0700289
Ray Milkey269ffb92014-04-03 14:43:30 -0700290 @Override
291 public void deleteOnosDevice(OnosDevice dev) {
292 Long mac = dev.getMacAddress().toLong();
293 eventChannel.removeEntry(mac);
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700294 floodlightProvider.publishUpdate(
295 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
Ray Milkey269ffb92014-04-03 14:43:30 -0700296 }
TeruUd1c5b652014-03-24 13:58:46 -0700297
Ray Milkey269ffb92014-04-03 14:43:30 -0700298 @Override
299 public void deleteOnosDeviceByMac(MACAddress mac) {
Pavlin Radoslavov63607792014-04-09 16:56:28 -0700300 OnosDevice deleteDevice = mapDevice.get(mac.toLong());
Ray Milkey269ffb92014-04-03 14:43:30 -0700301 deleteOnosDevice(deleteDevice);
302 }
303
304 @Override
305 public void addOnosDevice(Long mac, OnosDevice dev) {
306 eventChannel.addEntry(mac, dev);
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700307 floodlightProvider.publishUpdate(
308 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
Ray Milkey269ffb92014-04-03 14:43:30 -0700309 }
310
311 @Override
312 public void entryAdded(OnosDevice dev) {
313 Long mac = dev.getMacAddress().toLong();
314 mapDevice.put(mac, dev);
TeruU28adcc32014-04-15 17:57:35 -0700315 log.debug("Device added into local Cache: device mac {}", mac);
Ray Milkey269ffb92014-04-03 14:43:30 -0700316 }
317
318 @Override
319 public void entryRemoved(OnosDevice dev) {
320 Long mac = dev.getMacAddress().toLong();
321 mapDevice.remove(mac);
TeruU28adcc32014-04-15 17:57:35 -0700322 log.debug("Device removed into local Cache: device mac {}", mac);
Ray Milkey269ffb92014-04-03 14:43:30 -0700323 }
324
325 @Override
326 public void entryUpdated(OnosDevice dev) {
327 Long mac = dev.getMacAddress().toLong();
328 mapDevice.put(mac, dev);
TeruU28adcc32014-04-15 17:57:35 -0700329 log.debug("Device updated into local Cache: device mac {}", mac);
Ray Milkey269ffb92014-04-03 14:43:30 -0700330 }
331
332 @Override
333 public void addOnosDeviceListener(IOnosDeviceListener listener) {
334 deviceListeners.add(listener);
335 }
336
337 @Override
338 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
339 deviceListeners.remove(listener);
340 }
TeruU28adcc32014-04-15 17:57:35 -0700341
342 private void setOnosDeviceManagerProperty(FloodlightModuleContext context) {
343 Map<String, String> configOptions = context.getConfigParams(this);
344 String cleanupsec = configOptions.get("cleanupsec");
345 String agingmsec = configOptions.get("agingmsec");
346 if (cleanupsec != null) {
347 cleanupSecondConfig = Integer.parseInt(cleanupsec);
348 log.debug("CLEANUP_SECOND is set to {}", cleanupSecondConfig);
349 }
350
351 if (agingmsec != null) {
352 agingMillisecConfig = Integer.parseInt(agingmsec);
353 log.debug("AGEING_MILLSEC is set to {}", agingMillisecConfig);
354 }
355 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800356}