blob: fd345194c67af92202dfdc0ab3b5eaeef38d41c4 [file] [log] [blame]
Jonathan Hart03102132014-07-01 23:22:04 -07001package net.onrc.onos.core.hostmanager;
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;
Jonathan Harte37e4e22014-05-13 19:12:02 -070027import net.onrc.onos.core.topology.ITopologyService;
TeruU5d2c9392014-06-09 20:02:02 -070028import net.onrc.onos.core.topology.Port;
Jonathan Harte37e4e22014-05-13 19:12:02 -070029import net.onrc.onos.core.topology.Topology;
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -070030import net.onrc.onos.core.util.Dpid;
31import net.onrc.onos.core.util.PortNumber;
Jonathan Hartd857ad62013-12-14 18:08:17 -080032
33import org.openflow.protocol.OFMessage;
34import org.openflow.protocol.OFPacketIn;
35import org.openflow.protocol.OFType;
TeruU80ce5062014-03-03 17:16:13 -080036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
Jonathan Hartd857ad62013-12-14 18:08:17 -080038
Jonathan Hart03102132014-07-01 23:22:04 -070039public class HostManager implements IFloodlightModule,
Ray Milkey269ffb92014-04-03 14:43:30 -070040 IOFMessageListener,
Jonathan Hart03102132014-07-01 23:22:04 -070041 IHostService {
TeruU28adcc32014-04-15 17:57:35 -070042
Jonathan Hart03102132014-07-01 23:22:04 -070043 private static final Logger log = LoggerFactory.getLogger(HostManager.class);
44 private static final long HOST_CLEANING_INITIAL_DELAY = 30;
TeruU28adcc32014-04-15 17:57:35 -070045 private int cleanupSecondConfig = 60 * 60;
46 private int agingMillisecConfig = 60 * 60 * 1000;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070047
Jonathan Hart03102132014-07-01 23:22:04 -070048 private CopyOnWriteArrayList<IHostListener> hostListeners;
Ray Milkey269ffb92014-04-03 14:43:30 -070049 private IFloodlightProviderService floodlightProvider;
Jonathan Hart83ce8c42014-06-02 00:07:06 -070050 private static final ScheduledExecutorService EXECUTOR_SERVICE =
51 Executors.newSingleThreadScheduledExecutor();
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070052
Jonathan Harte37e4e22014-05-13 19:12:02 -070053 private ITopologyService topologyService;
54 private Topology topology;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070055
Jonathan Hart03102132014-07-01 23:22:04 -070056 public enum HostUpdateType {
TeruU80ce5062014-03-03 17:16:13 -080057 ADD, DELETE, UPDATE;
58 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070059
Jonathan Hart03102132014-07-01 23:22:04 -070060 private class HostUpdate implements IUpdate {
61 private final Host host;
62 private final HostUpdateType type;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070063
Jonathan Hart03102132014-07-01 23:22:04 -070064 public HostUpdate(Host host, HostUpdateType type) {
65 this.host = host;
Ray Milkey269ffb92014-04-03 14:43:30 -070066 this.type = type;
67 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070068
Ray Milkey269ffb92014-04-03 14:43:30 -070069 @Override
70 public void dispatch() {
Jonathan Hart03102132014-07-01 23:22:04 -070071 if (type == HostUpdateType.ADD) {
72 for (IHostListener listener : hostListeners) {
73 listener.hostAdded(host);
Ray Milkey269ffb92014-04-03 14:43:30 -070074 }
Jonathan Hart03102132014-07-01 23:22:04 -070075 } else if (type == HostUpdateType.DELETE) {
76 for (IHostListener listener : hostListeners) {
77 listener.hostRemoved(host);
Ray Milkey269ffb92014-04-03 14:43:30 -070078 }
79 }
80 }
81 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -070082
Ray Milkey269ffb92014-04-03 14:43:30 -070083 @Override
84 public String getName() {
Jonathan Hart03102132014-07-01 23:22:04 -070085 return "hostmanager";
Ray Milkey269ffb92014-04-03 14:43:30 -070086 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080087
Ray Milkey269ffb92014-04-03 14:43:30 -070088 @Override
89 public boolean isCallbackOrderingPrereq(OFType type, String name) {
90 // We want link discovery to consume LLDP first otherwise we'll
Jonathan Hart03102132014-07-01 23:22:04 -070091 // end up reading bad host info from LLDP packets
Ray Milkey269ffb92014-04-03 14:43:30 -070092 return type == OFType.PACKET_IN && "linkdiscovery".equals(name);
93 }
Jonathan Hartd857ad62013-12-14 18:08:17 -080094
Ray Milkey269ffb92014-04-03 14:43:30 -070095 @Override
96 public boolean isCallbackOrderingPostreq(OFType type, String name) {
97 return type == OFType.PACKET_IN &&
98 ("proxyarpmanager".equals(name) || "onosforwarding".equals(name));
99 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800100
Ray Milkey269ffb92014-04-03 14:43:30 -0700101 @Override
102 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
Pavlin Radoslavov0b88a262014-04-10 15:43:27 -0700103 if (msg.getType().equals(OFType.PACKET_IN) &&
Patrick Liuab1e6062014-05-05 11:12:13 -0700104 (msg instanceof OFPacketIn)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700105 OFPacketIn pi = (OFPacketIn) msg;
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700106
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 Ethernet eth = IFloodlightProviderService.bcStore.
108 get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700109
Ray Milkey269ffb92014-04-03 14:43:30 -0700110 return processPacketIn(sw, pi, eth);
111 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700112
Ray Milkey269ffb92014-04-03 14:43:30 -0700113 return Command.CONTINUE;
114 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700115
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700116 // This "protected" modifier is for unit test.
117 // The above "receive" method couldn't be tested
118 // because of IFloodlightProviderService static final field.
TeruU28adcc32014-04-15 17:57:35 -0700119 protected Command processPacketIn(IOFSwitch sw, OFPacketIn pi, Ethernet eth) {
TeruU5d2c9392014-06-09 20:02:02 -0700120 if (log.isTraceEnabled()) {
121 log.trace("Receive PACKET_IN swId {}, portId {}", sw.getId(), pi.getInPort());
122 }
123
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700124 final Dpid dpid = new Dpid(sw.getId());
125 final PortNumber portNum = new PortNumber(pi.getInPort());
TeruU80ce5062014-03-03 17:16:13 -0800126 Long mac = eth.getSourceMAC().toLong();
127
Jonathan Hart03102132014-07-01 23:22:04 -0700128 Host srcHost =
129 getSourceHostFromPacket(eth, dpid.value(), portNum.value());
TeruU80ce5062014-03-03 17:16:13 -0800130
Jonathan Hart03102132014-07-01 23:22:04 -0700131 if (srcHost == null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 return Command.STOP;
TeruU80ce5062014-03-03 17:16:13 -0800133 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700134
Jonathan Hart03102132014-07-01 23:22:04 -0700135 // If the switch port we try to attach a new host already has a link,
136 // then don't add the host
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700137 // TODO We probably don't need to check this here, it should be done in
138 // the Topology module.
TeruU5d2c9392014-06-09 20:02:02 -0700139 topology.acquireReadLock();
140 try {
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700141 if (topology.getOutgoingLink(dpid, portNum) != null ||
142 topology.getIncomingLink(dpid, portNum) != null) {
Jonathan Hart03102132014-07-01 23:22:04 -0700143 log.debug("Not adding host {} as " +
TeruU5d2c9392014-06-09 20:02:02 -0700144 "there is a link on the port: dpid {} port {}",
Jonathan Hart03102132014-07-01 23:22:04 -0700145 srcHost.getMacAddress(), dpid, portNum);
TeruU5d2c9392014-06-09 20:02:02 -0700146 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700147 }
TeruU5d2c9392014-06-09 20:02:02 -0700148 } finally {
149 topology.releaseReadLock();
TeruU80ce5062014-03-03 17:16:13 -0800150 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700151
Jonathan Hart03102132014-07-01 23:22:04 -0700152 addHost(mac, srcHost);
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700153
Ray Milkey269ffb92014-04-03 14:43:30 -0700154 if (log.isTraceEnabled()) {
Jonathan Hart03102132014-07-01 23:22:04 -0700155 log.trace("Add host info: {}", srcHost);
TeruU80ce5062014-03-03 17:16:13 -0800156 }
Jonathan Hartd857ad62013-12-14 18:08:17 -0800157 return Command.CONTINUE;
Ray Milkey269ffb92014-04-03 14:43:30 -0700158 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700159
Jonathan Hart03102132014-07-01 23:22:04 -0700160 // Thread to delete hosts periodically.
161 // Remove all hosts from the map first and then finally delete hosts
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700162 // from the DB.
163
Jonathan Hart03102132014-07-01 23:22:04 -0700164 // TODO This should be sharded based on host 'owner' (i.e. the instance
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700165 // that owns the switch it is attached to). Currently any instance can
Jonathan Hart03102132014-07-01 23:22:04 -0700166 // issue deletes for any host, which permits race conditions and could
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700167 // cause the Topology replicas to diverge.
Jonathan Hart03102132014-07-01 23:22:04 -0700168 private class HostCleaner implements Runnable {
Ray Milkey269ffb92014-04-03 14:43:30 -0700169 @Override
170 public void run() {
Jonathan Hart03102132014-07-01 23:22:04 -0700171 log.debug("called HostCleaner");
TeruU5d2c9392014-06-09 20:02:02 -0700172 topology.acquireReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700173 try {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700174 Set<net.onrc.onos.core.topology.Host> deleteSet = new HashSet<>();
175 for (net.onrc.onos.core.topology.Host host : topology.getHosts()) {
TeruU5d2c9392014-06-09 20:02:02 -0700176 long now = System.currentTimeMillis();
Jonathan Hart03102132014-07-01 23:22:04 -0700177 if ((now - host.getLastSeenTime() > agingMillisecConfig)) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700178 if (log.isTraceEnabled()) {
Jonathan Hart03102132014-07-01 23:22:04 -0700179 log.trace("Removing host info: mac {}, now {}, lastSeenTime {}, diff {}",
180 host.getMacAddress(), now, host.getLastSeenTime(), now - host.getLastSeenTime());
Ray Milkey269ffb92014-04-03 14:43:30 -0700181 }
Jonathan Hart03102132014-07-01 23:22:04 -0700182 deleteSet.add(host);
Ray Milkey269ffb92014-04-03 14:43:30 -0700183 }
184 }
Yuta HIGUCHIe7eac182014-03-19 19:18:30 -0700185
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700186 for (net.onrc.onos.core.topology.Host host : deleteSet) {
Jonathan Hart03102132014-07-01 23:22:04 -0700187 deleteHostByMac(host.getMacAddress());
Ray Milkey269ffb92014-04-03 14:43:30 -0700188 }
189 } catch (Exception e) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700190 // Any exception thrown by the task will prevent the Executor
191 // from running the next iteration, so we need to catch and log
192 // all exceptions here.
Jonathan Hart03102132014-07-01 23:22:04 -0700193 log.error("Exception in host cleanup thread:", e);
TeruU5d2c9392014-06-09 20:02:02 -0700194 } finally {
195 topology.releaseReadLock();
Ray Milkey269ffb92014-04-03 14:43:30 -0700196 }
197 }
198 }
TeruU80ce5062014-03-03 17:16:13 -0800199
Jonathan Hartd857ad62013-12-14 18:08:17 -0800200 /**
Jonathan Hart03102132014-07-01 23:22:04 -0700201 * Parse a host from an {@link Ethernet} packet.
Ray Milkey269ffb92014-04-03 14:43:30 -0700202 *
Jonathan Hartd857ad62013-12-14 18:08:17 -0800203 * @param eth the packet to parse
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700204 * @param swdpid the switch on which the packet arrived
205 * @param port the port on which the packet arrived
Jonathan Hart03102132014-07-01 23:22:04 -0700206 * @return the host from the packet
Jonathan Hartd857ad62013-12-14 18:08:17 -0800207 */
Jonathan Hart03102132014-07-01 23:22:04 -0700208 protected Host getSourceHostFromPacket(Ethernet eth,
209 long swdpid, 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 Hart03102132014-07-01 23:22:04 -0700218 return new Host(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>>();
Jonathan Hart03102132014-07-01 23:22:04 -0700229 services.add(IHostService.class);
Ray Milkey269ffb92014-04-03 14:43:30 -0700230 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>();
Jonathan Hart03102132014-07-01 23:22:04 -0700237 impls.put(IHostService.class, this);
Ray Milkey269ffb92014-04-03 14:43:30 -0700238 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);
Jonathan Hart03102132014-07-01 23:22:04 -0700254 hostListeners = new CopyOnWriteArrayList<IHostListener>();
Jonathan Harte37e4e22014-05-13 19:12:02 -0700255 topologyService = context.getServiceImpl(ITopologyService.class);
256 topology = topologyService.getTopology();
TeruU28adcc32014-04-15 17:57:35 -0700257
Jonathan Hart03102132014-07-01 23:22:04 -0700258 setHostManagerProperties(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 Hart03102132014-07-01 23:22:04 -0700264 EXECUTOR_SERVICE.scheduleAtFixedRate(new HostCleaner(),
265 HOST_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
Jonathan Hart03102132014-07-01 23:22:04 -0700269 public void deleteHost(Host host) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700270 floodlightProvider.publishUpdate(
Jonathan Hart03102132014-07-01 23:22:04 -0700271 new HostUpdate(host, HostUpdateType.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
Jonathan Hart03102132014-07-01 23:22:04 -0700275 public void deleteHostByMac(MACAddress mac) {
276 Host deleteHost = null;
TeruU5d2c9392014-06-09 20:02:02 -0700277 topology.acquireReadLock();
278 try {
Yuta HIGUCHIbfc77f02014-07-14 22:50:25 -0700279 net.onrc.onos.core.topology.Host host = topology.getHostByMac(mac);
TeruU5d2c9392014-06-09 20:02:02 -0700280
Jonathan Hart03102132014-07-01 23:22:04 -0700281 for (Port switchPort : host.getAttachmentPoints()) {
TeruU5d2c9392014-06-09 20:02:02 -0700282 // We don't handle vlan now and multiple attachment points.
Jonathan Hart03102132014-07-01 23:22:04 -0700283 deleteHost = new Host(host.getMacAddress(),
TeruU5d2c9392014-06-09 20:02:02 -0700284 null,
Yuta HIGUCHI8f3dfa32014-06-25 00:14:25 -0700285 switchPort.getDpid().value(),
286 (long) switchPort.getNumber().value(),
Jonathan Hart03102132014-07-01 23:22:04 -0700287 new Date(host.getLastSeenTime()));
TeruU5d2c9392014-06-09 20:02:02 -0700288 break;
289 }
290 } finally {
291 topology.releaseReadLock();
292 }
293
Jonathan Hart03102132014-07-01 23:22:04 -0700294 if (deleteHost != null) {
295 deleteHost(deleteHost);
TeruU5d2c9392014-06-09 20:02:02 -0700296 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700297 }
298
299 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700300 public void addHost(Long mac, Host host) {
Jonathan Hart83ce8c42014-06-02 00:07:06 -0700301 floodlightProvider.publishUpdate(
Jonathan Hart03102132014-07-01 23:22:04 -0700302 new HostUpdate(host, HostUpdateType.ADD));
Ray Milkey269ffb92014-04-03 14:43:30 -0700303 }
304
305 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700306 public void addHostListener(IHostListener listener) {
307 hostListeners.add(listener);
Ray Milkey269ffb92014-04-03 14:43:30 -0700308 }
309
310 @Override
Jonathan Hart03102132014-07-01 23:22:04 -0700311 public void removeHostListener(IHostListener listener) {
312 hostListeners.remove(listener);
Ray Milkey269ffb92014-04-03 14:43:30 -0700313 }
TeruU28adcc32014-04-15 17:57:35 -0700314
Jonathan Hart03102132014-07-01 23:22:04 -0700315 private void setHostManagerProperties(FloodlightModuleContext context) {
TeruU28adcc32014-04-15 17:57:35 -0700316 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}