blob: 30ffb1948b5901f535cc216fdb2c11ed63ae4a95 [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;
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -070031import net.onrc.onos.core.util.Dpid;
32import net.onrc.onos.core.util.PortNumber;
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,
TeruU5d2c9392014-06-09 20:02:02 -070042 IOnosDeviceService {
TeruU28adcc32014-04-15 17:57:35 -070043
Ray Milkeyec838942014-04-09 11:28:43 -070044 private static final Logger log = LoggerFactory.getLogger(OnosDeviceManager.class);
TeruU28adcc32014-04-15 17:57:35 -070045 private static final long DEVICE_CLEANING_INITIAL_DELAY = 30;
46 private int cleanupSecondConfig = 60 * 60;
47 private int agingMillisecConfig = 60 * 60 * 1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070048
Ray Milkey269ffb92014-04-03 14:43:30 -070049 private CopyOnWriteArrayList<IOnosDeviceListener> deviceListeners;
50 private IFloodlightProviderService floodlightProvider;
Jonathan Hart83ce8c42014-06-02 00:07:06 -070051 private static final ScheduledExecutorService EXECUTOR_SERVICE =
52 Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070053
Jonathan Harte37e4e22014-05-13 19:12:02 -070054 private ITopologyService topologyService;
55 private Topology topology;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070056
TeruU80ce5062014-03-03 17:16:13 -080057 public enum OnosDeviceUpdateType {
58 ADD, DELETE, UPDATE;
59 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070060
Ray Milkey269ffb92014-04-03 14:43:30 -070061 private class OnosDeviceUpdate implements IUpdate {
Patrick Liuab1e6062014-05-05 11:12:13 -070062 private final OnosDevice device;
63 private final OnosDeviceUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070064
Ray Milkey269ffb92014-04-03 14:43:30 -070065 public OnosDeviceUpdate(OnosDevice device, OnosDeviceUpdateType type) {
66 this.device = device;
67 this.type = type;
68 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070069
Ray Milkey269ffb92014-04-03 14:43:30 -070070 @Override
71 public void dispatch() {
72 if (type == OnosDeviceUpdateType.ADD) {
73 for (IOnosDeviceListener listener : deviceListeners) {
74 listener.onosDeviceAdded(device);
75 }
76 } else if (type == OnosDeviceUpdateType.DELETE) {
77 for (IOnosDeviceListener listener : deviceListeners) {
78 listener.onosDeviceRemoved(device);
79 }
80 }
81 }
82 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070083
Ray Milkey269ffb92014-04-03 14:43:30 -070084 @Override
85 public String getName() {
86 return "onosdevicemanager";
87 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080088
Ray Milkey269ffb92014-04-03 14:43:30 -070089 @Override
90 public boolean isCallbackOrderingPrereq(OFType type, String name) {
91 // We want link discovery to consume LLDP first otherwise we'll
92 // end up reading bad device info from LLDP packets
93 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
94 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080095
Ray Milkey269ffb92014-04-03 14:43:30 -070096 @Override
97 public boolean isCallbackOrderingPostreq(OFType type, String name) {
98 return type == OFType.PACKET_IN &&
99 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
100 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800101
Ray Milkey269ffb92014-04-03 14:43:30 -0700102 @Override
103 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700104 if (msg.getType().equals(OFType.PACKET_IN) &&
Patrick Liuab1e6062014-05-05 11:12:13 -0700105 (msg instanceof OFPacketIn)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700107
Ray Milkey269ffb92014-04-03 14:43:30 -0700108 Ethernet eth = IFloodlightProviderService.bcStore.
109 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700110
Ray Milkey269ffb92014-04-03 14:43:30 -0700111 return processPacketIn(sw, pi, eth);
112 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700113
Ray Milkey269ffb92014-04-03 14:43:30 -0700114 return Command.CONTINUE;
115 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700116
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700117 // This "protected" modifier is for unit test.
118 // The above "receive" method couldn't be tested
119 // because of IFloodlightProviderService static final field.
TeruU28adcc32014-04-15 17:57:35 -0700120 protected Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU5d2c9392014-06-09 20:02:02 -0700121 if (log.isTraceEnabled()) {
122 log.trace("Receive PACKET_IN swId {}, portId {}", sw.getId(), pi.getInPort());
123 }
124
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700125 final Dpid dpid = new Dpid(sw.getId());
126 final PortNumber portNum = new PortNumber(pi.getInPort());
TeruU80ce5062014-03-03 17:16:13 -0800127 Long mac = eth.getSourceMAC().toLong();
128
Jonathan Hartd857ad62013-12-14 18:08:17 -0800129 OnosDevice srcDevice =
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700130 getSourceDeviceFromPacket(eth, dpid.value(), portNum.value());
TeruU80ce5062014-03-03 17:16:13 -0800131
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 if (srcDevice == null) {
133 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800134 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700135
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700136 // If the switch port we try to attach a new device already has a link,
137 // then don't add the device
138 // TODO We probably don't need to check this here, it should be done in
139 // the Topology module.
TeruU5d2c9392014-06-09 20:02:02 -0700140 topology.acquireReadLock();
141 try {
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700142 if (topology.getOutgoingLink(dpid, portNum) != null ||
143 topology.getIncomingLink(dpid, portNum) != null) {
TeruU5d2c9392014-06-09 20:02:02 -0700144 log.debug("Stop adding OnosDevice {} as " +
145 "there is a link on the port: dpid {} port {}",
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700146 srcDevice.getMacAddress(), dpid, portNum);
TeruU5d2c9392014-06-09 20:02:02 -0700147 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700148 }
TeruU5d2c9392014-06-09 20:02:02 -0700149 } finally {
150 topology.releaseReadLock();
TeruU80ce5062014-03-03 17:16:13 -0800151 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700152
TeruU80ce5062014-03-03 17:16:13 -0800153 addOnosDevice(mac, srcDevice);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700154
Ray Milkey269ffb92014-04-03 14:43:30 -0700155 if (log.isTraceEnabled()) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700156 log.trace("Add device info: {}", srcDevice);
TeruU80ce5062014-03-03 17:16:13 -0800157 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800158 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700159 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700160
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700161 // Thread to delete devices periodically.
162 // Remove all devices from the map first and then finally delete devices
163 // from the DB.
164
165 // TODO This should be sharded based on device 'owner' (i.e. the instance
166 // that owns the switch it is attached to). Currently any instance can
167 // issue deletes for any device, which permits race conditions and could
168 // cause the Topology replicas to diverge.
Ray Milkey269ffb92014-04-03 14:43:30 -0700169 private class CleanDevice implements Runnable {
170 @Override
171 public void run() {
172 log.debug("called CleanDevice");
TeruU5d2c9392014-06-09 20:02:02 -0700173 topology.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700174 try {
TeruU5d2c9392014-06-09 20:02:02 -0700175 Set<Device> deleteSet = new HashSet<Device>();
176 for (Device dev : topology.getDevices()) {
177 long now = System.currentTimeMillis();
178 if ((now - dev.getLastSeenTime() > agingMillisecConfig)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700179 if (log.isTraceEnabled()) {
TeruU5d2c9392014-06-09 20:02:02 -0700180 log.trace("Removing device info: mac {}, now {}, lastSeenTime {}, diff {}",
181 dev.getMacAddress(), now, dev.getLastSeenTime(), now - dev.getLastSeenTime());
Ray Milkey269ffb92014-04-03 14:43:30 -0700182 }
183 deleteSet.add(dev);
184 }
185 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700186
TeruU5d2c9392014-06-09 20:02:02 -0700187 for (Device dev : deleteSet) {
188 deleteOnosDeviceByMac(dev.getMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -0700189 }
190 } catch (Exception e) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700191 // Any exception thrown by the task will prevent the Executor
192 // from running the next iteration, so we need to catch and log
193 // all exceptions here.
194 log.error("Exception in device cleanup thread:", e);
TeruU5d2c9392014-06-09 20:02:02 -0700195 } finally {
196 topology.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700197 }
198 }
199 }
TeruU80ce5062014-03-03 17:16:13 -0800200
Jonathan Hartd857ad62013-12-14 18:08:17 -0800201 /**
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700202 * Parse a device from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700203 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800204 * @param eth the packet to parse
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700205 * @param swdpid the switch on which the packet arrived
206 * @param port the port on which the packet arrived
207 * @return the device from the packet
Jonathan Hartd857ad62013-12-14 18:08:17 -0800208 */
Patrick Liuab1e6062014-05-05 11:12:13 -0700209 protected OnosDevice getSourceDeviceFromPacket(Ethernet eth,
210 long swdpid,
TeruU5d2c9392014-06-09 20:02:02 -0700211 long port) {
Jonathan Hart7ab71612014-05-27 13:37:31 -0700212 MACAddress sourceMac = eth.getSourceMAC();
Jonathan Hartd857ad62013-12-14 18:08:17 -0800213
Jonathan Hart7ab71612014-05-27 13:37:31 -0700214 // Ignore broadcast/multicast source
215 if (sourceMac.isBroadcast() || sourceMac.isBroadcast()) {
Jonathan Hartd857ad62013-12-14 18:08:17 -0800216 return null;
Ray Milkeyb29e6262014-04-09 16:02:14 -0700217 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800218
219 short vlan = eth.getVlanID();
Jonathan Hart7ab71612014-05-27 13:37:31 -0700220 return new OnosDevice(sourceMac,
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 ((vlan >= 0) ? vlan : null),
Ray Milkey269ffb92014-04-03 14:43:30 -0700222 swdpid,
223 port,
224 new Date());
Jonathan Hartd857ad62013-12-14 18:08:17 -0800225 }
226
Ray Milkey269ffb92014-04-03 14:43:30 -0700227 @Override
228 public Collection<Class<? extends IFloodlightService>> getModuleServices() {
229 List<Class<? extends IFloodlightService>> services =
230 new ArrayList<Class<? extends IFloodlightService>>();
231 services.add(IOnosDeviceService.class);
232 return services;
233 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800234
TeruUd1c5b652014-03-24 13:58:46 -0700235 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700236 public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
237 Map<Class<? extends IFloodlightService>, IFloodlightService> impls =
238 new HashMap<Class<? extends IFloodlightService>, IFloodlightService>();
239 impls.put(IOnosDeviceService.class, this);
240 return impls;
241 }
242
TeruUd1c5b652014-03-24 13:58:46 -0700243 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700244 public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
245 List<Class<? extends IFloodlightService>> dependencies =
246 new ArrayList<Class<? extends IFloodlightService>>();
247 dependencies.add(IFloodlightProviderService.class);
Jonathan Harte37e4e22014-05-13 19:12:02 -0700248 dependencies.add(ITopologyService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700249 return dependencies;
250 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700251
Ray Milkey269ffb92014-04-03 14:43:30 -0700252 @Override
253 public void init(FloodlightModuleContext context)
254 throws FloodlightModuleException {
255 floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700256 deviceListeners = new CopyOnWriteArrayList<IOnosDeviceListener>();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700257 topologyService = context.getServiceImpl(ITopologyService.class);
258 topology = topologyService.getTopology();
TeruU28adcc32014-04-15 17:57:35 -0700259
260 setOnosDeviceManagerProperty(context);
Ray Milkey269ffb92014-04-03 14:43:30 -0700261 }
TeruU80ce5062014-03-03 17:16:13 -0800262
Ray Milkey269ffb92014-04-03 14:43:30 -0700263 @Override
264 public void startUp(FloodlightModuleContext context) {
265 floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700266 EXECUTOR_SERVICE.scheduleAtFixedRate(new CleanDevice(),
267 DEVICE_CLEANING_INITIAL_DELAY, cleanupSecondConfig, TimeUnit.SECONDS);
Ray Milkey269ffb92014-04-03 14:43:30 -0700268 }
TeruUd1c5b652014-03-24 13:58:46 -0700269
Ray Milkey269ffb92014-04-03 14:43:30 -0700270 @Override
271 public void deleteOnosDevice(OnosDevice dev) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700272 floodlightProvider.publishUpdate(
273 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.DELETE));
Ray Milkey269ffb92014-04-03 14:43:30 -0700274 }
TeruUd1c5b652014-03-24 13:58:46 -0700275
Ray Milkey269ffb92014-04-03 14:43:30 -0700276 @Override
277 public void deleteOnosDeviceByMac(MACAddress mac) {
TeruU5d2c9392014-06-09 20:02:02 -0700278 OnosDevice deleteDevice = null;
279 topology.acquireReadLock();
280 try {
281 Device dev = topology.getDeviceByMac(mac);
282
283 for (Port switchPort : dev.getAttachmentPoints()) {
284 // We don't handle vlan now and multiple attachment points.
285 deleteDevice = new OnosDevice(dev.getMacAddress(),
286 null,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700287 switchPort.getDpid().value(),
288 (long) switchPort.getNumber().value(),
TeruU5d2c9392014-06-09 20:02:02 -0700289 new Date(dev.getLastSeenTime()));
290 break;
291 }
292 } finally {
293 topology.releaseReadLock();
294 }
295
296 if (deleteDevice != null) {
297 deleteOnosDevice(deleteDevice);
298 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700299 }
300
301 @Override
302 public void addOnosDevice(Long mac, OnosDevice dev) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700303 floodlightProvider.publishUpdate(
304 new OnosDeviceUpdate(dev, OnosDeviceUpdateType.ADD));
Ray Milkey269ffb92014-04-03 14:43:30 -0700305 }
306
307 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700308 public void addOnosDeviceListener(IOnosDeviceListener listener) {
309 deviceListeners.add(listener);
310 }
311
312 @Override
313 public void deleteOnosDeviceListener(IOnosDeviceListener listener) {
314 deviceListeners.remove(listener);
315 }
TeruU28adcc32014-04-15 17:57:35 -0700316
317 private void setOnosDeviceManagerProperty(FloodlightModuleContext context) {
318 Map<String, String> configOptions = context.getConfigParams(this);
319 String cleanupsec = configOptions.get("cleanupsec");
320 String agingmsec = configOptions.get("agingmsec");
321 if (cleanupsec != null) {
322 cleanupSecondConfig = Integer.parseInt(cleanupsec);
323 log.debug("CLEANUP_SECOND is set to {}", cleanupSecondConfig);
324 }
325
326 if (agingmsec != null) {
327 agingMillisecConfig = Integer.parseInt(agingmsec);
328 log.debug("AGEING_MILLSEC is set to {}", agingMillisecConfig);
329 }
330 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800331}