blob: 74f080266428f4098c7afad153403ee1c01a7b70 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080017
18package net.floodlightcontroller.core.internal;
19
20import java.io.FileInputStream;
21import java.io.IOException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080022import java.net.InetSocketAddress;
Jonathan Hartd10008d2013-02-23 17:04:08 -080023import java.net.UnknownHostException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080024import java.nio.channels.ClosedChannelException;
Jonathan Hartd10008d2013-02-23 17:04:08 -080025import java.util.ArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080026import java.util.Collection;
27import java.util.Collections;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080028import java.util.HashMap;
29import java.util.HashSet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080030import java.util.List;
31import java.util.Map;
32import java.util.Map.Entry;
33import java.util.Properties;
34import java.util.Set;
35import java.util.Stack;
36import java.util.concurrent.BlockingQueue;
37import java.util.concurrent.ConcurrentHashMap;
38import java.util.concurrent.ConcurrentMap;
39import java.util.concurrent.CopyOnWriteArraySet;
40import java.util.concurrent.Executors;
41import java.util.concurrent.Future;
42import java.util.concurrent.LinkedBlockingQueue;
43import java.util.concurrent.RejectedExecutionException;
44import java.util.concurrent.TimeUnit;
45import java.util.concurrent.TimeoutException;
46
47import net.floodlightcontroller.core.FloodlightContext;
48import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080049import net.floodlightcontroller.core.IListener.Command;
Jonathan Hartd10008d2013-02-23 17:04:08 -080050import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080051import net.floodlightcontroller.core.IOFSwitch;
52import net.floodlightcontroller.core.IOFSwitchFilter;
53import net.floodlightcontroller.core.IOFSwitchListener;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070054import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080055import net.floodlightcontroller.core.annotations.LogMessageDoc;
56import net.floodlightcontroller.core.annotations.LogMessageDocs;
57import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
58import net.floodlightcontroller.core.util.ListenerDispatcher;
59import net.floodlightcontroller.core.web.CoreWebRoutable;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080061import net.floodlightcontroller.threadpool.IThreadPoolService;
Jonathan Hart23701d12014-04-03 10:45:48 -070062import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070063import net.onrc.onos.core.main.IOFSwitchPortListener;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070064import net.onrc.onos.core.packet.Ethernet;
65import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070066import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070067import net.onrc.onos.core.registry.RegistryException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080068
69import org.jboss.netty.bootstrap.ServerBootstrap;
70import org.jboss.netty.buffer.ChannelBuffer;
71import org.jboss.netty.buffer.ChannelBuffers;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080072import org.jboss.netty.channel.ChannelHandlerContext;
73import org.jboss.netty.channel.ChannelPipelineFactory;
74import org.jboss.netty.channel.ChannelStateEvent;
75import org.jboss.netty.channel.ChannelUpstreamHandler;
76import org.jboss.netty.channel.Channels;
77import org.jboss.netty.channel.ExceptionEvent;
78import org.jboss.netty.channel.MessageEvent;
79import org.jboss.netty.channel.group.ChannelGroup;
80import org.jboss.netty.channel.group.DefaultChannelGroup;
81import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
82import org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler;
83import org.jboss.netty.handler.timeout.IdleStateEvent;
84import org.jboss.netty.handler.timeout.ReadTimeoutException;
85import org.openflow.protocol.OFEchoReply;
86import org.openflow.protocol.OFError;
87import org.openflow.protocol.OFError.OFBadActionCode;
88import org.openflow.protocol.OFError.OFBadRequestCode;
89import org.openflow.protocol.OFError.OFErrorType;
90import org.openflow.protocol.OFError.OFFlowModFailedCode;
91import org.openflow.protocol.OFError.OFHelloFailedCode;
92import org.openflow.protocol.OFError.OFPortModFailedCode;
93import org.openflow.protocol.OFError.OFQueueOpFailedCode;
94import org.openflow.protocol.OFFeaturesReply;
95import org.openflow.protocol.OFGetConfigReply;
96import org.openflow.protocol.OFMessage;
97import org.openflow.protocol.OFPacketIn;
98import org.openflow.protocol.OFPhysicalPort;
Pankaj Berde6a4075d2013-01-22 16:42:54 -080099import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
Pankaj Berde6debb042013-01-16 18:04:32 -0800100import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800101import org.openflow.protocol.OFPortStatus;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800102import org.openflow.protocol.OFPortStatus.OFPortReason;
103import org.openflow.protocol.OFSetConfig;
104import org.openflow.protocol.OFStatisticsRequest;
105import org.openflow.protocol.OFSwitchConfig;
106import org.openflow.protocol.OFType;
107import org.openflow.protocol.OFVendor;
108import org.openflow.protocol.factory.BasicFactory;
109import org.openflow.protocol.factory.MessageParseException;
110import org.openflow.protocol.statistics.OFDescriptionStatistics;
111import org.openflow.protocol.statistics.OFStatistics;
112import org.openflow.protocol.statistics.OFStatisticsType;
113import org.openflow.protocol.vendor.OFBasicVendorDataType;
114import org.openflow.protocol.vendor.OFBasicVendorId;
115import org.openflow.protocol.vendor.OFVendorId;
116import org.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800117import org.openflow.vendor.nicira.OFNiciraVendorData;
118import org.openflow.vendor.nicira.OFRoleReplyVendorData;
119import org.openflow.vendor.nicira.OFRoleRequestVendorData;
120import org.openflow.vendor.nicira.OFRoleVendorData;
121import org.slf4j.Logger;
122import org.slf4j.LoggerFactory;
123
124
125/**
126 * The main controller class. Handles all setup and network listeners
Ray Milkey269ffb92014-04-03 14:43:30 -0700127 * <p/>
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700128 * Extensions made by ONOS are:
Ray Milkey269ffb92014-04-03 14:43:30 -0700129 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
130 * Available as net.onrc.onos.core.main.IOFSwitchPortListener
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700131 * - Distributed ownership control of switch through RegistryService(IControllerRegistryService)
Pavlin Radoslavova653e9f2013-10-16 03:08:52 -0700132 * - Register ONOS services. (IControllerRegistryService)
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700133 * - Additional DEBUG logs
134 * - Try using hostname as controller ID, when ID was not explicitly given.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800135 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800136public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700137
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700138 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139
Ray Milkey269ffb92014-04-03 14:43:30 -0700140 private static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800141 "The controller could not communicate with the system database.";
Ray Milkey269ffb92014-04-03 14:43:30 -0700142
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800143 protected BasicFactory factory;
144 protected ConcurrentMap<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700145 ListenerDispatcher<OFType, IOFMessageListener>>
146 messageListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800147 // The activeSwitches map contains only those switches that are actively
148 // being controlled by us -- it doesn't contain switches that are
149 // in the slave role
150 protected ConcurrentHashMap<Long, IOFSwitch> activeSwitches;
151 // connectedSwitches contains all connected switches, including ones where
152 // we're a slave controller. We need to keep track of them so that we can
153 // send role request messages to switches when our role changes to master
154 // We add a switch to this set after it successfully completes the
155 // handshake. Access to this Set needs to be synchronized with roleChanger
156 protected HashSet<OFSwitchImpl> connectedSwitches;
Ray Milkey269ffb92014-04-03 14:43:30 -0700157
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800158 // The controllerNodeIPsCache maps Controller IDs to their IP address.
159 // It's only used by handleControllerNodeIPsChanged
160 protected HashMap<String, String> controllerNodeIPsCache;
Ray Milkey269ffb92014-04-03 14:43:30 -0700161
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800162 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800163 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700164
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800165 // Module dependencies
166 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800167 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800168 protected IControllerRegistryService registryService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700169
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800170 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700171
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800172 // Configuration options
173 protected int openFlowPort = 6633;
174 protected int workerThreads = 0;
175 // The id for this controller node. Should be unique for each controller
176 // node in a controller cluster.
177 protected String controllerId = "localhost";
Ray Milkey269ffb92014-04-03 14:43:30 -0700178
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800179 // The current role of the controller.
180 // If the controller isn't configured to support roles, then this is null.
181 protected Role role;
182 // A helper that handles sending and timeout handling for role requests
183 protected RoleChanger roleChanger;
Ray Milkey269ffb92014-04-03 14:43:30 -0700184
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800185 // Start time of the controller
186 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700187
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800188 // Flag to always flush flow table on switch reconnect (HA or otherwise)
189 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800191 // Perf. related configuration
192 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
193 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700194 protected static final boolean ALWAYS_DECODE_ETH = true;
195
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800196 public enum SwitchUpdateType {
197 ADDED,
198 REMOVED,
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700199 PORTCHANGED,
200 PORTADDED,
201 PORTREMOVED
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800202 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700203
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700205 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700206 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800207 */
208 protected class SwitchUpdate implements IUpdate {
209 public IOFSwitch sw;
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700210 public OFPhysicalPort port; // Added by ONOS
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 public SwitchUpdateType switchUpdateType;
Ray Milkey269ffb92014-04-03 14:43:30 -0700212
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800213 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
214 this.sw = sw;
215 this.switchUpdateType = switchUpdateType;
216 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700217
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700218 public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
219 this.sw = sw;
220 this.port = port;
221 this.switchUpdateType = switchUpdateType;
222 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700223
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800224 public void dispatch() {
225 if (log.isTraceEnabled()) {
226 log.trace("Dispatching switch update {} {}",
227 sw, switchUpdateType);
228 }
229 if (switchListeners != null) {
230 for (IOFSwitchListener listener : switchListeners) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700231 switch (switchUpdateType) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800232 case ADDED:
233 listener.addedSwitch(sw);
234 break;
235 case REMOVED:
236 listener.removedSwitch(sw);
237 break;
238 case PORTCHANGED:
239 listener.switchPortChanged(sw.getId());
240 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700241 case PORTADDED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 if (listener instanceof IOFSwitchPortListener) {
243 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
244 }
245 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700246 case PORTREMOVED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700247 if (listener instanceof IOFSwitchPortListener) {
248 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
249 }
250 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700251 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700252 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800253 }
254 }
255 }
256 }
257 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700258
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800259 // ***************
260 // Getters/Setters
Jonathan Hart20fab1a2013-12-12 11:06:50 -0800261 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700262
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800263 public void setRestApiService(IRestApiService restApi) {
264 this.restApi = restApi;
265 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700266
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800267 public void setThreadPoolService(IThreadPoolService tp) {
268 this.threadPool = tp;
269 }
270
Ray Milkey269ffb92014-04-03 14:43:30 -0700271 public void setMastershipService(IControllerRegistryService serviceImpl) {
272 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700273 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700274
275 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
276 this.linkDiscovery = linkDiscovery;
277 }
278
279 public void publishUpdate(IUpdate update) {
280 try {
281 this.updates.put(update);
282 } catch (InterruptedException e) {
283 log.error("Failure adding update to queue", e);
284 }
285 }
286
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800287 // **********************
288 // ChannelUpstreamHandler
289 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700290
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291 /**
292 * Return a new channel handler for processing a switch connections
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800294 * @param state The channel state object for the connection
295 * @return the new channel handler
296 */
297 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
298 return new OFChannelHandler(state);
299 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700300
Jonathan Hartcc957a02013-02-26 10:39:04 -0800301 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 @Override
303 public void controlChanged(long dpid, boolean hasControl) {
304 log.info("Role change callback for switch {}, hasControl {}",
305 HexString.toHexString(dpid), hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800306
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 synchronized (roleChanger) {
308 OFSwitchImpl sw = null;
309 for (OFSwitchImpl connectedSw : connectedSwitches) {
310 if (connectedSw.getId() == dpid) {
311 sw = connectedSw;
312 break;
313 }
314 }
315 if (sw == null) {
316 log.warn("Switch {} not found in connected switches",
317 HexString.toHexString(dpid));
318 return;
319 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800320
Ray Milkey269ffb92014-04-03 14:43:30 -0700321 Role role = null;
322
323 /*
324 * issue #229
325 * Cannot rely on sw.getRole() as it can be behind due to pending
326 * role changes in the queue. Just submit it and late the RoleChanger
327 * handle duplicates.
328 */
Pankaj Berde01939e92013-03-08 14:38:27 -0800329
Ray Milkey269ffb92014-04-03 14:43:30 -0700330 if (hasControl) {
331 role = Role.MASTER;
332 } else {
333 role = Role.SLAVE;
334 }
335
336 log.debug("Sending role request {} msg to {}", role, sw);
337 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
338 swList.add(sw);
339 roleChanger.submitRequest(swList, role);
340
341 }
342
343 }
Jonathan Hartcc957a02013-02-26 10:39:04 -0800344 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700345
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800346 /**
347 * Channel handler deals with the switch connection and dispatches
348 * switch messages to the appropriate locations.
Ray Milkey269ffb92014-04-03 14:43:30 -0700349 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800350 * @author readams
351 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700352 protected class OFChannelHandler
353 extends IdleStateAwareChannelUpstreamHandler {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800354 protected OFSwitchImpl sw;
355 protected OFChannelState state;
Ray Milkey269ffb92014-04-03 14:43:30 -0700356
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800357 public OFChannelHandler(OFChannelState state) {
358 this.state = state;
359 }
360
361 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 @LogMessageDoc(message = "New switch connection from {ip address}",
363 explanation = "A new switch has connected from the " +
364 "specified IP address")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800365 public void channelConnected(ChannelHandlerContext ctx,
366 ChannelStateEvent e) throws Exception {
367 log.info("New switch connection from {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700368 e.getChannel().getRemoteAddress());
369
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800370 sw = new OFSwitchImpl();
371 sw.setChannel(e.getChannel());
372 sw.setFloodlightProvider(Controller.this);
373 sw.setThreadPoolService(threadPool);
Ray Milkey269ffb92014-04-03 14:43:30 -0700374
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800375 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
376 msglist.add(factory.getMessage(OFType.HELLO));
377 e.getChannel().write(msglist);
378
379 }
380
381 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700382 @LogMessageDoc(message = "Disconnected switch {switch information}",
383 explanation = "The specified switch has disconnected.")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800384 public void channelDisconnected(ChannelHandlerContext ctx,
385 ChannelStateEvent e) throws Exception {
386 if (sw != null && state.hsState == HandshakeState.READY) {
387 if (activeSwitches.containsKey(sw.getId())) {
388 // It's safe to call removeSwitch even though the map might
389 // not contain this particular switch but another with the
390 // same DPID
391 removeSwitch(sw);
392 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700393 synchronized (roleChanger) {
394 if (controlRequested) {
395 registryService.releaseControl(sw.getId());
396 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800397 connectedSwitches.remove(sw);
398 }
399 sw.setConnected(false);
400 }
401 log.info("Disconnected switch {}", sw);
402 }
403
404 @Override
405 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700406 @LogMessageDoc(level = "ERROR",
407 message = "Disconnecting switch {switch} due to read timeout",
408 explanation = "The connected switch has failed to send any " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800409 "messages or respond to echo requests",
Ray Milkey269ffb92014-04-03 14:43:30 -0700410 recommendation = LogMessageDoc.CHECK_SWITCH),
411 @LogMessageDoc(level = "ERROR",
412 message = "Disconnecting switch {switch}: failed to " +
413 "complete handshake",
414 explanation = "The switch did not respond correctly " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800415 "to handshake messages",
Ray Milkey269ffb92014-04-03 14:43:30 -0700416 recommendation = LogMessageDoc.CHECK_SWITCH),
417 @LogMessageDoc(level = "ERROR",
418 message = "Disconnecting switch {switch} due to IO Error: {}",
419 explanation = "There was an error communicating with the switch",
420 recommendation = LogMessageDoc.CHECK_SWITCH),
421 @LogMessageDoc(level = "ERROR",
422 message = "Disconnecting switch {switch} due to switch " +
423 "state error: {error}",
424 explanation = "The switch sent an unexpected message",
425 recommendation = LogMessageDoc.CHECK_SWITCH),
426 @LogMessageDoc(level = "ERROR",
427 message = "Disconnecting switch {switch} due to " +
428 "message parse failure",
429 explanation = "Could not parse a message from the switch",
430 recommendation = LogMessageDoc.CHECK_SWITCH),
431 @LogMessageDoc(level = "ERROR",
432 message = "Could not process message: queue full",
433 explanation = "OpenFlow messages are arriving faster than " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800434 " the controller can process them.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700435 recommendation = LogMessageDoc.CHECK_CONTROLLER),
436 @LogMessageDoc(level = "ERROR",
437 message = "Error while processing message " +
438 "from switch {switch} {cause}",
439 explanation = "An error occurred processing the switch message",
440 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800441 })
442 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
443 throws Exception {
444 if (e.getCause() instanceof ReadTimeoutException) {
445 // switch timeout
446 log.error("Disconnecting switch {} due to read timeout", sw);
447 ctx.getChannel().close();
448 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700449 log.error("Disconnecting switch {}: failed to complete handshake",
450 sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800451 ctx.getChannel().close();
452 } else if (e.getCause() instanceof ClosedChannelException) {
453 //log.warn("Channel for sw {} already closed", sw);
454 } else if (e.getCause() instanceof IOException) {
455 log.error("Disconnecting switch {} due to IO Error: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700456 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800457 ctx.getChannel().close();
458 } else if (e.getCause() instanceof SwitchStateException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700459 log.error("Disconnecting switch {} due to switch state error: {}",
460 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800461 ctx.getChannel().close();
462 } else if (e.getCause() instanceof MessageParseException) {
463 log.error("Disconnecting switch " + sw +
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 " due to message parse failure",
465 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800466 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800467 } else if (e.getCause() instanceof RejectedExecutionException) {
468 log.warn("Could not process message: queue full");
469 } else {
470 log.error("Error while processing message from switch " + sw,
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800472 ctx.getChannel().close();
473 }
474 }
475
476 @Override
477 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
478 throws Exception {
479 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
480 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
481 e.getChannel().write(msglist);
482 }
483
484 @Override
485 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
486 throws Exception {
487 if (e.getMessage() instanceof List) {
488 @SuppressWarnings("unchecked")
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490
491 for (OFMessage ofm : msglist) {
492 try {
493 processOFMessage(ofm);
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 } catch (Exception ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800495 // We are the last handler in the stream, so run the
496 // exception through the channel again by passing in
497 // ctx.getChannel().
498 Channels.fireExceptionCaught(ctx.getChannel(), ex);
499 }
500 }
501
502 // Flush all flow-mods/packet-out generated from this "train"
503 OFSwitchImpl.flush_all();
504 }
505 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700506
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800507 /**
508 * Process the request for the switch description
509 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700510 @LogMessageDoc(level = "ERROR",
511 message = "Exception in reading description " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800512 " during handshake {exception}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700513 explanation = "Could not process the switch description string",
514 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800515 void processSwitchDescReply() {
516 try {
517 // Read description, if it has been updated
518 @SuppressWarnings("unchecked")
519 Future<List<OFStatistics>> desc_future =
Ray Milkey269ffb92014-04-03 14:43:30 -0700520 (Future<List<OFStatistics>>) sw.
521 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
522 List<OFStatistics> values =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800523 desc_future.get(0, TimeUnit.MILLISECONDS);
524 if (values != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700525 OFDescriptionStatistics description =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800526 new OFDescriptionStatistics();
Ray Milkey269ffb92014-04-03 14:43:30 -0700527 ChannelBuffer data =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800528 ChannelBuffers.buffer(description.getLength());
529 for (OFStatistics f : values) {
530 f.writeTo(data);
531 description.readFrom(data);
532 break; // SHOULD be a list of length 1
533 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700534 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
535 description);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800536 sw.setSwitchProperties(description);
537 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800538 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700539
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800540 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
541 state.hasDescription = true;
542 checkSwitchReady();
Ray Milkey269ffb92014-04-03 14:43:30 -0700543 } catch (InterruptedException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800544 // Ignore
Ray Milkey269ffb92014-04-03 14:43:30 -0700545 } catch (TimeoutException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800546 // Ignore
547 } catch (Exception ex) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700548 log.error("Exception in reading description " +
549 " during handshake", ex);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800550 }
551 }
552
553 /**
554 * Send initial switch setup information that we need before adding
555 * the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700556 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800557 * @throws IOException
558 */
559 void sendHelloConfiguration() throws IOException {
560 // Send initial Features Request
Ray Milkey269ffb92014-04-03 14:43:30 -0700561 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800562 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
563 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700564
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800565 /**
566 * Send the configuration requests we can only do after we have
567 * the features reply
Ray Milkey269ffb92014-04-03 14:43:30 -0700568 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800569 * @throws IOException
570 */
571 void sendFeatureReplyConfiguration() throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700572 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800573 // Ensure we receive the full packet via PacketIn
574 OFSetConfig config = (OFSetConfig) factory
575 .getMessage(OFType.SET_CONFIG);
576 config.setMissSendLength((short) 0xffff)
Ray Milkey269ffb92014-04-03 14:43:30 -0700577 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800578 sw.write(config, null);
579 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
580 null);
581
582 // Get Description to set switch-specific flags
583 OFStatisticsRequest req = new OFStatisticsRequest();
584 req.setStatisticType(OFStatisticsType.DESC);
585 req.setLengthU(req.getLengthU());
Ray Milkey269ffb92014-04-03 14:43:30 -0700586 Future<List<OFStatistics>> dfuture =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800587 sw.getStatistics(req);
588 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
589 dfuture);
590
591 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700592
593 volatile Boolean controlRequested = Boolean.FALSE;
594
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595 protected void checkSwitchReady() {
596 if (state.hsState == HandshakeState.FEATURES_REPLY &&
597 state.hasDescription && state.hasGetConfigReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700598
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800599 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700600 log.debug("Handshake with {} complete", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700601
602 synchronized (roleChanger) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800603 // We need to keep track of all of the switches that are connected
604 // to the controller, in any role, so that we can later send the
605 // role request messages when the controller role changes.
606 // We need to be synchronized while doing this: we must not
607 // send a another role request to the connectedSwitches until
608 // we were able to add this new switch to connectedSwitches
609 // *and* send the current role to the new switch.
610 connectedSwitches.add(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700611
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800612 if (role != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700613 //Put the switch in SLAVE mode until we know we have control
614 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
615 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
616 swList.add(sw);
617 roleChanger.submitRequest(swList, Role.SLAVE);
618
619 //Request control of the switch from the global registry
620 try {
621 controlRequested = Boolean.TRUE;
622 registryService.requestControl(sw.getId(),
623 new RoleChangeCallback());
624 } catch (RegistryException e) {
625 log.debug("Registry error: {}", e.getMessage());
626 controlRequested = Boolean.FALSE;
627 }
628
629
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800630 // Send a role request if role support is enabled for the controller
631 // This is a probe that we'll use to determine if the switch
632 // actually supports the role request message. If it does we'll
633 // get back a role reply message. If it doesn't we'll get back an
634 // OFError message.
635 // If role is MASTER we will promote switch to active
636 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800637 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800638 log.debug("This controller's role is {}, " +
639 "sending initial role request msg to {}",
640 role, sw);
641 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
642 swList.add(sw);
643 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800644 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700645 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800646 // Role supported not enabled on controller (for now)
647 // automatically promote switch to active state.
Ray Milkey269ffb92014-04-03 14:43:30 -0700648 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800649 "not sending role request msg to {}",
650 role, sw);
651 // Need to clear FlowMods before we add the switch
652 // and dispatch updates otherwise we have a race condition.
653 sw.clearAllFlowMods();
654 addSwitch(sw);
655 state.firstRoleReplyReceived = true;
656 }
657 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700658 if (!controlRequested) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700659 // yield to allow other thread(s) to release control
660 try {
661 Thread.sleep(10);
662 } catch (InterruptedException e) {
663 // Ignore interruptions
664 }
665 // safer to bounce the switch to reconnect here than proceeding further
666 log.debug("Closing {} because we weren't able to request control " +
667 "successfully" + sw);
668 sw.channel.close();
Pankaj Berde99fcee12013-03-18 09:41:53 -0700669 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800670 }
671 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700672
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800673 /* Handle a role reply message we received from the switch. Since
674 * netty serializes message dispatch we don't need to synchronize
675 * against other receive operations from the same switch, so no need
676 * to synchronize addSwitch(), removeSwitch() operations from the same
677 * connection.
678 * FIXME: However, when a switch with the same DPID connects we do
679 * need some synchronization. However, handling switches with same
680 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
681 * removedSwitch notification):1
682 *
683 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700684 @LogMessageDoc(level = "ERROR",
685 message = "Invalid role value in role reply message",
686 explanation = "Was unable to set the HA role (master or slave) " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800687 "for the controller.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700688 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800689 protected void handleRoleReplyMessage(OFVendor vendorMessage,
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 OFRoleReplyVendorData roleReplyVendorData) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800691 // Map from the role code in the message to our role enum
692 int nxRole = roleReplyVendorData.getRole();
693 Role role = null;
694 switch (nxRole) {
695 case OFRoleVendorData.NX_ROLE_OTHER:
696 role = Role.EQUAL;
697 break;
698 case OFRoleVendorData.NX_ROLE_MASTER:
699 role = Role.MASTER;
700 break;
701 case OFRoleVendorData.NX_ROLE_SLAVE:
702 role = Role.SLAVE;
703 break;
704 default:
705 log.error("Invalid role value in role reply message");
706 sw.getChannel().close();
707 return;
708 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700709
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800710 log.debug("Handling role reply for role {} from {}. " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700711 "Controller's role is {} ",
712 new Object[]{role, sw, Controller.this.role}
713 );
714
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800715 sw.deliverRoleReply(vendorMessage.getXid(), role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700716
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800717 boolean isActive = activeSwitches.containsKey(sw.getId());
718 if (!isActive && sw.isActive()) {
719 // Transition from SLAVE to MASTER.
Ray Milkey269ffb92014-04-03 14:43:30 -0700720
721 if (!state.firstRoleReplyReceived ||
722 getAlwaysClearFlowsOnSwAdd()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800723 // This is the first role-reply message we receive from
724 // this switch or roles were disabled when the switch
725 // connected:
726 // Delete all pre-existing flows for new connections to
727 // the master
728 //
729 // FIXME: Need to think more about what the test should
730 // be for when we flush the flow-table? For example,
731 // if all the controllers are temporarily in the backup
732 // role (e.g. right after a failure of the master
733 // controller) at the point the switch connects, then
734 // all of the controllers will initially connect as
735 // backup controllers and not flush the flow-table.
736 // Then when one of them is promoted to master following
737 // the master controller election the flow-table
738 // will still not be flushed because that's treated as
739 // a failover event where we don't want to flush the
740 // flow-table. The end result would be that the flow
741 // table for a newly connected switch is never
742 // flushed. Not sure how to handle that case though...
743 sw.clearAllFlowMods();
744 log.debug("First role reply from master switch {}, " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700745 "clear FlowTable to active switch list",
746 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800747 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700748
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800749 // Some switches don't seem to update us with port
750 // status messages while in slave role.
Jonathan Hart2fa28062013-11-25 20:16:28 -0800751 //readSwitchPortStateFromStorage(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700752
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800753 // Only add the switch to the active switch list if
754 // we're not in the slave role. Note that if the role
755 // attribute is null, then that means that the switch
756 // doesn't support the role request messages, so in that
757 // case we're effectively in the EQUAL role and the
758 // switch should be included in the active switch list.
759 addSwitch(sw);
760 log.debug("Added master switch {} to active switch list",
Ray Milkey269ffb92014-04-03 14:43:30 -0700761 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800762
Ray Milkey269ffb92014-04-03 14:43:30 -0700763 } else if (isActive && !sw.isActive()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800764 // Transition from MASTER to SLAVE: remove switch
765 // from active switch list.
766 log.debug("Removed slave switch {} from active switch" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700767 " list", HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800768 removeSwitch(sw);
769 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700770
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800771 // Indicate that we have received a role reply message.
772 state.firstRoleReplyReceived = true;
773 }
774
775 protected boolean handleVendorMessage(OFVendor vendorMessage) {
776 boolean shouldHandleMessage = false;
777 int vendor = vendorMessage.getVendor();
778 switch (vendor) {
779 case OFNiciraVendorData.NX_VENDOR_ID:
780 OFNiciraVendorData niciraVendorData =
Ray Milkey269ffb92014-04-03 14:43:30 -0700781 (OFNiciraVendorData) vendorMessage.getVendorData();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800782 int dataType = niciraVendorData.getDataType();
783 switch (dataType) {
784 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
785 OFRoleReplyVendorData roleReplyVendorData =
786 (OFRoleReplyVendorData) niciraVendorData;
Ray Milkey269ffb92014-04-03 14:43:30 -0700787 handleRoleReplyMessage(vendorMessage,
788 roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800789 break;
790 default:
791 log.warn("Unhandled Nicira VENDOR message; " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700792 "data type = {}", dataType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800793 break;
794 }
795 break;
796 default:
797 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
798 break;
799 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700800
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800801 return shouldHandleMessage;
802 }
803
804 /**
805 * Dispatch an Openflow message from a switch to the appropriate
806 * handler.
Ray Milkey269ffb92014-04-03 14:43:30 -0700807 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800808 * @param m The message to process
809 * @throws IOException
Ray Milkey269ffb92014-04-03 14:43:30 -0700810 * @throws SwitchStateException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800811 */
812 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700813 @LogMessageDoc(level = "WARN",
814 message = "Config Reply from {switch} has " +
815 "miss length set to {length}",
816 explanation = "The controller requires that the switch " +
817 "use a miss length of 0xffff for correct " +
818 "function",
819 recommendation = "Use a different switch to ensure " +
820 "correct function"),
821 @LogMessageDoc(level = "WARN",
822 message = "Received ERROR from sw {switch} that "
823 + "indicates roles are not supported "
824 + "but we have received a valid "
825 + "role reply earlier",
826 explanation = "The switch sent a confusing message to the" +
827 "controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800828 })
829 protected void processOFMessage(OFMessage m)
830 throws IOException, SwitchStateException {
831 boolean shouldHandleMessage = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700832
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800833 switch (m.getType()) {
834 case HELLO:
835 if (log.isTraceEnabled())
836 log.trace("HELLO from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700837
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800838 if (state.hsState.equals(HandshakeState.START)) {
839 state.hsState = HandshakeState.HELLO;
840 sendHelloConfiguration();
841 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700842 throw new SwitchStateException("Unexpected HELLO from "
843 + sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800844 }
845 break;
846 case ECHO_REQUEST:
847 OFEchoReply reply =
Ray Milkey269ffb92014-04-03 14:43:30 -0700848 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800849 reply.setXid(m.getXid());
850 sw.write(reply, null);
851 break;
852 case ECHO_REPLY:
853 break;
854 case FEATURES_REPLY:
855 if (log.isTraceEnabled())
856 log.trace("Features Reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700857
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800858 sw.setFeaturesReply((OFFeaturesReply) m);
859 if (state.hsState.equals(HandshakeState.HELLO)) {
860 sendFeatureReplyConfiguration();
861 state.hsState = HandshakeState.FEATURES_REPLY;
862 // uncomment to enable "dumb" switches like cbench
863 // state.hsState = HandshakeState.READY;
864 // addSwitch(sw);
865 } else {
866 // return results to rest api caller
867 sw.deliverOFFeaturesReply(m);
868 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800869 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800870 }
871 break;
872 case GET_CONFIG_REPLY:
873 if (log.isTraceEnabled())
874 log.trace("Get config reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700875
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800876 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
877 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
878 throw new SwitchStateException(em);
879 }
880 OFGetConfigReply cr = (OFGetConfigReply) m;
Ray Milkey269ffb92014-04-03 14:43:30 -0700881 if (cr.getMissSendLength() == (short) 0xffff) {
882 log.trace("Config Reply from {} confirms " +
883 "miss length set to 0xffff", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800884 } else {
885 log.warn("Config Reply from {} has " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700886 "miss length set to {}",
887 sw, cr.getMissSendLength() & 0xffff);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800888 }
889 state.hasGetConfigReply = true;
890 checkSwitchReady();
891 break;
892 case VENDOR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700893 shouldHandleMessage = handleVendorMessage((OFVendor) m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800894 break;
895 case ERROR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700896 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800897 // TODO: we need better error handling. Especially for
898 // request/reply style message (stats, roles) we should have
899 // a unified way to lookup the xid in the error message.
900 // This will probable involve rewriting the way we handle
901 // request/reply style messages.
902 OFError error = (OFError) m;
903 boolean shouldLogError = true;
904 // TODO: should we check that firstRoleReplyReceived is false,
905 // i.e., check only whether the first request fails?
906 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
907 boolean isBadVendorError =
Ray Milkey269ffb92014-04-03 14:43:30 -0700908 (error.getErrorType() == OFError.OFErrorType.
909 OFPET_BAD_REQUEST.getValue());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800910 // We expect to receive a bad vendor error when
911 // we're connected to a switch that doesn't support
912 // the Nicira vendor extensions (i.e. not OVS or
913 // derived from OVS). By protocol, it should also be
914 // BAD_VENDOR, but too many switch implementations
915 // get it wrong and we can already check the xid()
916 // so we can ignore the type with confidence that this
917 // is not a spurious error
918 shouldLogError = !isBadVendorError;
919 if (isBadVendorError) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700920 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800921 if (state.firstRoleReplyReceived && (role != null)) {
922 log.warn("Received ERROR from sw {} that "
Ray Milkey269ffb92014-04-03 14:43:30 -0700923 + "indicates roles are not supported "
924 + "but we have received a valid "
925 + "role reply earlier", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800926 }
927 state.firstRoleReplyReceived = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700928 Role requestedRole =
929 sw.deliverRoleRequestNotSupportedEx(error.getXid());
930 synchronized (roleChanger) {
931 if (sw.role == null && Controller.this.role == Role.SLAVE) {
932 //This will now never happen. The Controller's role
933 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800934 // the switch doesn't understand role request
935 // messages and the current controller role is
936 // slave. We need to disconnect the switch.
937 // @see RoleChanger for rationale
Ray Milkey269ffb92014-04-03 14:43:30 -0700938 log.warn("Closing {} channel because controller's role " +
939 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800940 sw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700941 } else if (sw.role == null && requestedRole == Role.MASTER) {
942 log.debug("Adding switch {} because we got an error" +
943 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800944 // Controller's role is master: add to
945 // active
946 // TODO: check if clearing flow table is
947 // right choice here.
948 // Need to clear FlowMods before we add the switch
949 // and dispatch updates otherwise we have a race condition.
950 // TODO: switch update is async. Won't we still have a potential
951 // race condition?
952 sw.clearAllFlowMods();
953 addSwitch(sw);
954 }
955 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700956 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800957 // TODO: Is this the right thing to do if we receive
958 // some other error besides a bad vendor error?
959 // Presumably that means the switch did actually
960 // understand the role request message, but there
961 // was some other error from processing the message.
962 // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED
963 // error code, but it doesn't look like the Nicira
964 // role request has that. Should check OVS source
965 // code to see if it's possible for any other errors
966 // to be returned.
967 // If we received an error the switch is not
968 // in the correct role, so we need to disconnect it.
969 // We could also resend the request but then we need to
970 // check if there are other pending request in which
971 // case we shouldn't resend. If we do resend we need
972 // to make sure that the switch eventually accepts one
973 // of our requests or disconnect the switch. This feels
974 // cumbersome.
Ray Milkey269ffb92014-04-03 14:43:30 -0700975 log.debug("Closing {} channel because we recieved an " +
976 "error other than BAD_VENDOR", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800977 sw.getChannel().close();
978 }
979 }
980 // Once we support OF 1.2, we'd add code to handle it here.
981 //if (error.getXid() == state.ofRoleRequestXid) {
982 //}
983 if (shouldLogError)
984 logError(sw, error);
985 break;
986 case STATS_REPLY:
Ray Milkey269ffb92014-04-03 14:43:30 -0700987 if (state.hsState.ordinal() <
988 HandshakeState.FEATURES_REPLY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800989 String em = "Unexpected STATS_REPLY from " + sw;
990 throw new SwitchStateException(em);
991 }
992 sw.deliverStatisticsReply(m);
993 if (sw.hasAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE)) {
994 processSwitchDescReply();
995 }
996 break;
997 case PORT_STATUS:
998 // We want to update our port state info even if we're in
999 // the slave role, but we only want to update storage if
1000 // we're the master (or equal).
1001 boolean updateStorage = state.hsState.
Ray Milkey269ffb92014-04-03 14:43:30 -07001002 equals(HandshakeState.READY) &&
1003 (sw.getRole() != Role.SLAVE);
1004 handlePortStatusMessage(sw, (OFPortStatus) m, updateStorage);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001005 shouldHandleMessage = true;
1006 break;
1007
1008 default:
1009 shouldHandleMessage = true;
1010 break;
1011 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001012
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001013 if (shouldHandleMessage) {
1014 sw.getListenerReadLock().lock();
1015 try {
1016 if (sw.isConnected()) {
1017 if (!state.hsState.equals(HandshakeState.READY)) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001018 log.debug("Ignoring message type {} received " +
1019 "from switch {} before switch is " +
1020 "fully configured.", m.getType(), sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001021 }
1022 // Check if the controller is in the slave role for the
1023 // switch. If it is, then don't dispatch the message to
1024 // the listeners.
1025 // TODO: Should we dispatch messages that we expect to
1026 // receive when we're in the slave role, e.g. port
1027 // status messages? Since we're "hiding" switches from
1028 // the listeners when we're in the slave role, then it
1029 // seems a little weird to dispatch port status messages
1030 // to them. On the other hand there might be special
1031 // modules that care about all of the connected switches
1032 // and would like to receive port status notifications.
1033 else if (sw.getRole() == Role.SLAVE) {
1034 // Don't log message if it's a port status message
1035 // since we expect to receive those from the switch
1036 // and don't want to emit spurious messages.
1037 if (m.getType() != OFType.PORT_STATUS) {
1038 log.debug("Ignoring message type {} received " +
1039 "from switch {} while in the slave role.",
1040 m.getType(), sw);
1041 }
1042 } else {
1043 handleMessage(sw, m, null);
1044 }
1045 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001046 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001047 sw.getListenerReadLock().unlock();
1048 }
1049 }
1050 }
1051 }
1052
1053 // ****************
1054 // Message handlers
1055 // ****************
Ray Milkey269ffb92014-04-03 14:43:30 -07001056
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001057 protected void handlePortStatusMessage(IOFSwitch sw,
1058 OFPortStatus m,
1059 boolean updateStorage) {
1060 short portNumber = m.getDesc().getPortNumber();
1061 OFPhysicalPort port = m.getDesc();
Ray Milkey269ffb92014-04-03 14:43:30 -07001062 if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
1063 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1064 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001065 sw.setPort(port);
Ray Milkey269ffb92014-04-03 14:43:30 -07001066 if (!portDown) {
1067 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1068 try {
1069 this.updates.put(update);
1070 } catch (InterruptedException e) {
1071 log.error("Failure adding update to queue", e);
1072 }
1073 } else {
1074 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1075 try {
1076 this.updates.put(update);
1077 } catch (InterruptedException e) {
1078 log.error("Failure adding update to queue", e);
1079 }
1080 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001081 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001082 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001083 log.debug("Port #{} modified for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001084 } else if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
1085 // XXX Workaround to prevent race condition where a link is detected
1086 // and attempted to be written to the database before the port is in
1087 // the database. We now suppress link discovery on ports until we're
1088 // sure they're in the database.
1089 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1090
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001091 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001092 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1093 try {
1094 this.updates.put(update);
1095 } catch (InterruptedException e) {
1096 log.error("Failure adding update to queue", e);
1097 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001098 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001099 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001100 log.debug("Port #{} added for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001101 } else if (m.getReason() ==
1102 (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001103 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001104 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1105 try {
1106 this.updates.put(update);
1107 } catch (InterruptedException e) {
1108 log.error("Failure adding update to queue", e);
1109 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001110 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001111 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001112 log.debug("Port #{} deleted for {}", portNumber, sw);
1113 }
1114 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1115 try {
1116 this.updates.put(update);
1117 } catch (InterruptedException e) {
1118 log.error("Failure adding update to queue", e);
1119 }
1120 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001121
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001122 /**
1123 * flcontext_cache - Keep a thread local stack of contexts
1124 */
1125 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
Ray Milkey269ffb92014-04-03 14:43:30 -07001126 new ThreadLocal<Stack<FloodlightContext>>() {
1127 @Override
1128 protected Stack<FloodlightContext> initialValue() {
1129 return new Stack<FloodlightContext>();
1130 }
1131 };
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001132
1133 /**
1134 * flcontext_alloc - pop a context off the stack, if required create a new one
Ray Milkey269ffb92014-04-03 14:43:30 -07001135 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001136 * @return FloodlightContext
1137 */
1138 protected static FloodlightContext flcontext_alloc() {
1139 FloodlightContext flcontext = null;
1140
1141 if (flcontext_cache.get().empty()) {
1142 flcontext = new FloodlightContext();
Ray Milkey269ffb92014-04-03 14:43:30 -07001143 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001144 flcontext = flcontext_cache.get().pop();
1145 }
1146
1147 return flcontext;
1148 }
1149
1150 /**
1151 * flcontext_free - Free the context to the current thread
Ray Milkey269ffb92014-04-03 14:43:30 -07001152 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001153 * @param flcontext
1154 */
1155 protected void flcontext_free(FloodlightContext flcontext) {
1156 flcontext.getStorage().clear();
1157 flcontext_cache.get().push(flcontext);
1158 }
1159
1160 /**
1161 * Handle replies to certain OFMessages, and pass others off to listeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001162 *
1163 * @param sw The switch for the message
1164 * @param m The message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001165 * @param bContext The floodlight context. If null then floodlight context would
Ray Milkey269ffb92014-04-03 14:43:30 -07001166 * be allocated in this function
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001167 * @throws IOException
1168 */
1169 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001170 @LogMessageDoc(level = "ERROR",
1171 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
1172 " field is empty.",
1173 explanation = "The switch sent an improperly-formatted PacketIn" +
1174 " message",
1175 recommendation = LogMessageDoc.CHECK_SWITCH),
1176 @LogMessageDoc(level = "WARN",
1177 message = "Unhandled OF Message: {} from {}",
1178 explanation = "The switch sent a message not handled by " +
1179 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001180 })
1181 protected void handleMessage(IOFSwitch sw, OFMessage m,
1182 FloodlightContext bContext)
1183 throws IOException {
1184 Ethernet eth = null;
1185
1186 switch (m.getType()) {
1187 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -07001188 OFPacketIn pi = (OFPacketIn) m;
1189
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001190 if (pi.getPacketData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001191 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1192 ") because the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001193 return;
1194 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001195
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001196 if (Controller.ALWAYS_DECODE_ETH) {
1197 eth = new Ethernet();
1198 eth.deserialize(pi.getPacketData(), 0,
1199 pi.getPacketData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001200 }
1201 // fall through to default case...
1202
1203 default:
Ray Milkey269ffb92014-04-03 14:43:30 -07001204
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001205 List<IOFMessageListener> listeners = null;
1206 if (messageListeners.containsKey(m.getType())) {
1207 listeners = messageListeners.get(m.getType()).
1208 getOrderedListeners();
1209 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001210
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001211 FloodlightContext bc = null;
1212 if (listeners != null) {
1213 // Check if floodlight context is passed from the calling
1214 // function, if so use that floodlight context, otherwise
1215 // allocate one
1216 if (bContext == null) {
1217 bc = flcontext_alloc();
1218 } else {
1219 bc = bContext;
1220 }
1221 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001222 IFloodlightProviderService.bcStore.put(bc,
1223 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001224 eth);
1225 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001226
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001227 // Get the starting time (overall and per-component) of
1228 // the processing chain for this packet if performance
1229 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -07001230
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001231 Command cmd;
1232 for (IOFMessageListener listener : listeners) {
1233 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001234 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001235 continue;
1236 }
1237 }
1238
mininet73e7fb72013-12-03 14:25:53 -08001239
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001240 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001241
Ray Milkey269ffb92014-04-03 14:43:30 -07001242
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001243 if (Command.STOP.equals(cmd)) {
1244 break;
1245 }
1246 }
mininet73e7fb72013-12-03 14:25:53 -08001247
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001248 } else {
1249 log.warn("Unhandled OF Message: {} from {}", m, sw);
1250 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001251
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001252 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1253 }
1254 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001255
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001256 /**
1257 * Log an OpenFlow error message from a switch
Ray Milkey269ffb92014-04-03 14:43:30 -07001258 *
1259 * @param sw The switch that sent the error
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001260 * @param error The error message
1261 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001262 @LogMessageDoc(level = "ERROR",
1263 message = "Error {error type} {error code} from {switch}",
1264 explanation = "The switch responded with an unexpected error" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001265 "to an OpenFlow message from the controller",
Ray Milkey269ffb92014-04-03 14:43:30 -07001266 recommendation = "This could indicate improper network operation. " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001267 "If the problem persists restarting the switch and " +
1268 "controller may help."
Ray Milkey269ffb92014-04-03 14:43:30 -07001269 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001270 protected void logError(IOFSwitch sw, OFError error) {
1271 int etint = 0xffff & error.getErrorType();
1272 if (etint < 0 || etint >= OFErrorType.values().length) {
1273 log.error("Unknown error code {} from sw {}", etint, sw);
1274 }
1275 OFErrorType et = OFErrorType.values()[etint];
1276 switch (et) {
1277 case OFPET_HELLO_FAILED:
Ray Milkey269ffb92014-04-03 14:43:30 -07001278 OFHelloFailedCode hfc =
1279 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1280 log.error("Error {} {} from {}", new Object[]{et, hfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001281 break;
1282 case OFPET_BAD_REQUEST:
Ray Milkey269ffb92014-04-03 14:43:30 -07001283 OFBadRequestCode brc =
1284 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1285 log.error("Error {} {} from {}", new Object[]{et, brc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001286 break;
1287 case OFPET_BAD_ACTION:
1288 OFBadActionCode bac =
Ray Milkey269ffb92014-04-03 14:43:30 -07001289 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1290 log.error("Error {} {} from {}", new Object[]{et, bac, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001291 break;
1292 case OFPET_FLOW_MOD_FAILED:
1293 OFFlowModFailedCode fmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001294 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1295 log.error("Error {} {} from {}", new Object[]{et, fmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001296 break;
1297 case OFPET_PORT_MOD_FAILED:
1298 OFPortModFailedCode pmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001299 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1300 log.error("Error {} {} from {}", new Object[]{et, pmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001301 break;
1302 case OFPET_QUEUE_OP_FAILED:
1303 OFQueueOpFailedCode qofc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001304 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1305 log.error("Error {} {} from {}", new Object[]{et, qofc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001306 break;
1307 default:
1308 break;
1309 }
1310 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001311
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001312 /**
1313 * Add a switch to the active switch list and call the switch listeners.
1314 * This happens either when a switch first connects (and the controller is
1315 * not in the slave role) or when the role of the controller changes from
1316 * slave to master.
Ray Milkey269ffb92014-04-03 14:43:30 -07001317 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001318 * @param sw the switch that has been added
1319 */
1320 // TODO: need to rethink locking and the synchronous switch update.
1321 // We can / should also handle duplicate DPIDs in connectedSwitches
Ray Milkey269ffb92014-04-03 14:43:30 -07001322 @LogMessageDoc(level = "ERROR",
1323 message = "New switch added {switch} for already-added switch {switch}",
1324 explanation = "A switch with the same DPID as another switch " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001325 "connected to the controller. This can be caused by " +
1326 "multiple switches configured with the same DPID, or " +
1327 "by a switch reconnected very quickly after " +
1328 "disconnecting.",
Ray Milkey269ffb92014-04-03 14:43:30 -07001329 recommendation = "If this happens repeatedly, it is likely there " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001330 "are switches with duplicate DPIDs on the network. " +
1331 "Reconfigure the appropriate switches. If it happens " +
1332 "very rarely, then it is likely this is a transient " +
1333 "network problem that can be ignored."
Ray Milkey269ffb92014-04-03 14:43:30 -07001334 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001335 protected void addSwitch(IOFSwitch sw) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001336 // XXX Workaround to prevent race condition where a link is detected
1337 // and attempted to be written to the database before the port is in
1338 // the database. We now suppress link discovery on ports until we're
1339 // sure they're in the database.
1340 for (OFPhysicalPort port : sw.getPorts()) {
1341 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1342 }
1343
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001344 // TODO: is it safe to modify the HashMap without holding
1345 // the old switch's lock?
1346 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1347 if (sw == oldSw) {
1348 // Note == for object equality, not .equals for value
1349 log.info("New add switch for pre-existing switch {}", sw);
1350 return;
1351 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001352
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001353 if (oldSw != null) {
1354 oldSw.getListenerWriteLock().lock();
1355 try {
1356 log.error("New switch added {} for already-added switch {}",
Ray Milkey269ffb92014-04-03 14:43:30 -07001357 sw, oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001358 // Set the connected flag to false to suppress calling
1359 // the listeners for this switch in processOFMessage
1360 oldSw.setConnected(false);
Ray Milkey269ffb92014-04-03 14:43:30 -07001361
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001362 oldSw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001363
Jonathan Hart2fa28062013-11-25 20:16:28 -08001364 //updateInactiveSwitchInfo(oldSw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001365
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001366 // we need to clean out old switch state definitively
1367 // before adding the new switch
1368 // FIXME: It seems not completely kosher to call the
1369 // switch listeners here. I thought one of the points of
1370 // having the asynchronous switch update mechanism was so
1371 // the addedSwitch and removedSwitch were always called
1372 // from a single thread to simplify concurrency issues
1373 // for the listener.
1374 if (switchListeners != null) {
1375 for (IOFSwitchListener listener : switchListeners) {
1376 listener.removedSwitch(oldSw);
1377 }
1378 }
1379 // will eventually trigger a removeSwitch(), which will cause
1380 // a "Not removing Switch ... already removed debug message.
1381 // TODO: Figure out a way to handle this that avoids the
1382 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001383 log.debug("Closing {} because a new IOFSwitch got added " +
Ray Milkey269ffb92014-04-03 14:43:30 -07001384 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001385 oldSw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -07001386 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001387 oldSw.getListenerWriteLock().unlock();
1388 }
1389 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001390
Jonathan Hart2fa28062013-11-25 20:16:28 -08001391 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001392 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1393 try {
1394 this.updates.put(update);
1395 } catch (InterruptedException e) {
1396 log.error("Failure adding update to queue", e);
1397 }
1398 }
1399
1400 /**
1401 * Remove a switch from the active switch list and call the switch listeners.
1402 * This happens either when the switch is disconnected or when the
1403 * controller's role for the switch changes from master to slave.
Ray Milkey269ffb92014-04-03 14:43:30 -07001404 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001405 * @param sw the switch that has been removed
1406 */
1407 protected void removeSwitch(IOFSwitch sw) {
1408 // No need to acquire the listener lock, since
1409 // this method is only called after netty has processed all
1410 // pending messages
1411 log.debug("removeSwitch: {}", sw);
1412 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1413 log.debug("Not removing switch {}; already removed", sw);
1414 return;
1415 }
1416 // We cancel all outstanding statistics replies if the switch transition
1417 // from active. In the future we might allow statistics requests
1418 // from slave controllers. Then we need to move this cancelation
1419 // to switch disconnect
1420 sw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001421
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001422 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1423 // here if role support is enabled. In that case if the switch is being
1424 // removed because we've been switched to being in the slave role, then I think
1425 // it's possible that the new master may have already been promoted to master
1426 // and written out the active switch state to storage. If we now execute
1427 // updateInactiveSwitchInfo we may wipe out all of the state that was
1428 // written out by the new master. Maybe need to revisit how we handle all
1429 // of the switch state that's written to storage.
Ray Milkey269ffb92014-04-03 14:43:30 -07001430
Jonathan Hart2fa28062013-11-25 20:16:28 -08001431 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001432 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1433 try {
1434 this.updates.put(update);
1435 } catch (InterruptedException e) {
1436 log.error("Failure adding update to queue", e);
1437 }
1438 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001439
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001440 // ***************
1441 // IFloodlightProvider
1442 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -07001443
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001444 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001445 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001446 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001447 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1448 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001449 if (ldd == null) {
1450 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1451 messageListeners.put(type, ldd);
1452 }
1453 ldd.addListener(type, listener);
1454 }
1455
1456 @Override
1457 public synchronized void removeOFMessageListener(OFType type,
1458 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001459 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1460 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001461 if (ldd != null) {
1462 ldd.removeListener(listener);
1463 }
1464 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001465
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001466 private void logListeners() {
1467 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -07001468 ListenerDispatcher<OFType,
1469 IOFMessageListener>> entry
1470 : messageListeners.entrySet()) {
1471
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001472 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -07001473 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001474 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -07001475
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001476 StringBuffer sb = new StringBuffer();
1477 sb.append("OFListeners for ");
1478 sb.append(type);
1479 sb.append(": ");
1480 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1481 sb.append(l.getName());
1482 sb.append(",");
1483 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001484 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001485 }
1486 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001487
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001488 public void removeOFMessageListeners(OFType type) {
1489 messageListeners.remove(type);
1490 }
1491
1492 @Override
1493 public Map<Long, IOFSwitch> getSwitches() {
1494 return Collections.unmodifiableMap(this.activeSwitches);
1495 }
1496
1497 @Override
1498 public void addOFSwitchListener(IOFSwitchListener listener) {
1499 this.switchListeners.add(listener);
1500 }
1501
1502 @Override
1503 public void removeOFSwitchListener(IOFSwitchListener listener) {
1504 this.switchListeners.remove(listener);
1505 }
1506
1507 @Override
1508 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001509 Map<OFType, List<IOFMessageListener>> lers =
1510 new HashMap<OFType, List<IOFMessageListener>>();
1511 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1512 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001513 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1514 }
1515 return Collections.unmodifiableMap(lers);
1516 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001517
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001518 @Override
1519 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001520 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
1521 "a null switch",
1522 explanation = "Failed to process a message because the switch " +
1523 " is no longer connected."),
1524 @LogMessageDoc(level = "ERROR",
1525 message = "Error reinjecting OFMessage on switch {switch}",
1526 explanation = "An I/O error occured while attempting to " +
1527 "process an OpenFlow message",
1528 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001529 })
1530 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1531 FloodlightContext bc) {
1532 if (sw == null) {
1533 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1534 return false;
1535 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001536
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001537 // FIXME: Do we need to be able to inject messages to switches
1538 // where we're the slave controller (i.e. they're connected but
1539 // not active)?
1540 // FIXME: Don't we need synchronization logic here so we're holding
1541 // the listener read lock when we call handleMessage? After some
1542 // discussions it sounds like the right thing to do here would be to
1543 // inject the message as a netty upstream channel event so it goes
1544 // through the normal netty event processing, including being
1545 // handled
1546 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -07001547
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001548 try {
1549 // Pass Floodlight context to the handleMessages()
1550 handleMessage(sw, msg, bc);
1551 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001552 log.error("Error reinjecting OFMessage on switch {}",
1553 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001554 return false;
1555 }
1556 return true;
1557 }
1558
1559 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001560 @LogMessageDoc(message = "Calling System.exit",
1561 explanation = "The controller is terminating")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001562 public synchronized void terminate() {
1563 log.info("Calling System.exit");
1564 System.exit(1);
1565 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001566
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001567 @Override
1568 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1569 // call the overloaded version with floodlight context set to null
1570 return injectOfMessage(sw, msg, null);
1571 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001572
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001573 @Override
1574 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1575 FloodlightContext bc) {
1576 if (log.isTraceEnabled()) {
1577 String str = OFMessage.getDataAsString(sw, m, bc);
1578 log.trace("{}", str);
1579 }
1580
1581 List<IOFMessageListener> listeners = null;
1582 if (messageListeners.containsKey(m.getType())) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001583 listeners =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001584 messageListeners.get(m.getType()).getOrderedListeners();
1585 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001586
1587 if (listeners != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001588 for (IOFMessageListener listener : listeners) {
1589 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001590 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001591 continue;
1592 }
1593 }
1594 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1595 break;
1596 }
1597 }
1598 }
1599 }
1600
1601 @Override
1602 public BasicFactory getOFMessageFactory() {
1603 return factory;
1604 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001605
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001606 @Override
1607 public String getControllerId() {
1608 return controllerId;
1609 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001610
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001611 // **************
1612 // Initialization
1613 // **************
1614
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001615 /**
1616 * Sets the initial role based on properties in the config params.
1617 * It looks for two different properties.
1618 * If the "role" property is specified then the value should be
1619 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1620 * controller is set to the specified value. If the "role" property
1621 * is not specified then it looks next for the "role.path" property.
1622 * In this case the value should be the path to a property file in
1623 * the file system that contains a property called "floodlight.role"
1624 * which can be one of the values listed above for the "role" property.
1625 * The idea behind the "role.path" mechanism is that you have some
1626 * separate heartbeat and master controller election algorithm that
1627 * determines the role of the controller. When a role transition happens,
1628 * it updates the current role in the file specified by the "role.path"
1629 * file. Then if floodlight restarts for some reason it can get the
1630 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001631 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001632 * @param configParams The config params for the FloodlightProvider service
1633 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001634 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001635 */
1636 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001637 @LogMessageDoc(message = "Controller role set to {role}",
1638 explanation = "Setting the initial HA role to "),
1639 @LogMessageDoc(level = "ERROR",
1640 message = "Invalid current role value: {role}",
1641 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001642 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001643 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001644 })
1645 protected Role getInitialRole(Map<String, String> configParams) {
1646 Role role = null;
1647 String roleString = configParams.get("role");
1648 if (roleString == null) {
1649 String rolePath = configParams.get("rolepath");
1650 if (rolePath != null) {
1651 Properties properties = new Properties();
1652 try {
1653 properties.load(new FileInputStream(rolePath));
1654 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001655 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001656 // Don't treat it as an error if the file specified by the
1657 // rolepath property doesn't exist. This lets us enable the
1658 // HA mechanism by just creating/setting the floodlight.role
1659 // property in that file without having to modify the
1660 // floodlight properties.
1661 }
1662 }
1663 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001664
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001665 if (roleString != null) {
1666 // Canonicalize the string to the form used for the enum constants
1667 roleString = roleString.trim().toUpperCase();
1668 try {
1669 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001670 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001671 log.error("Invalid current role value: {}", roleString);
1672 }
1673 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001674
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001675 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001676
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001677 return role;
1678 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001679
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001680 /**
1681 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001682 *
1683 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001684 */
1685 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001686 @LogMessageDoc(message = "Listening for switch connections on {address}",
1687 explanation = "The controller is ready and listening for new" +
1688 " switch connections"),
1689 @LogMessageDoc(message = "Storage exception in controller " +
1690 "updates loop; terminating process",
1691 explanation = ERROR_DATABASE,
1692 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1693 @LogMessageDoc(level = "ERROR",
1694 message = "Exception in controller updates loop",
1695 explanation = "Failed to dispatch controller event",
1696 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001697 })
1698 public void run() {
1699 if (log.isDebugEnabled()) {
1700 logListeners();
1701 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001702
1703 try {
1704 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001705
1706 bootstrap.setOption("reuseAddr", true);
1707 bootstrap.setOption("child.keepAlive", true);
1708 bootstrap.setOption("child.tcpNoDelay", true);
1709 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1710
Ray Milkey269ffb92014-04-03 14:43:30 -07001711 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001712 new OpenflowPipelineFactory(this, null);
1713 bootstrap.setPipelineFactory(pfact);
1714 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1715 final ChannelGroup cg = new DefaultChannelGroup();
1716 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001717
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001718 log.info("Listening for switch connections on {}", sa);
1719 } catch (Exception e) {
1720 throw new RuntimeException(e);
1721 }
1722
1723 // main loop
1724 while (true) {
1725 try {
1726 IUpdate update = updates.take();
1727 update.dispatch();
1728 } catch (InterruptedException e) {
1729 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001730 } catch (Exception e) {
1731 log.error("Exception in controller updates loop", e);
1732 }
1733 }
1734 }
1735
1736 private ServerBootstrap createServerBootStrap() {
1737 if (workerThreads == 0) {
1738 return new ServerBootstrap(
1739 new NioServerSocketChannelFactory(
1740 Executors.newCachedThreadPool(),
1741 Executors.newCachedThreadPool()));
1742 } else {
1743 return new ServerBootstrap(
1744 new NioServerSocketChannelFactory(
1745 Executors.newCachedThreadPool(),
1746 Executors.newCachedThreadPool(), workerThreads));
1747 }
1748 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001749
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001750 public void setConfigParams(Map<String, String> configParams) {
1751 String ofPort = configParams.get("openflowport");
1752 if (ofPort != null) {
1753 this.openFlowPort = Integer.parseInt(ofPort);
1754 }
1755 log.debug("OpenFlow port set to {}", this.openFlowPort);
1756 String threads = configParams.get("workerthreads");
1757 if (threads != null) {
1758 this.workerThreads = Integer.parseInt(threads);
1759 }
1760 log.debug("Number of worker threads set to {}", this.workerThreads);
1761 String controllerId = configParams.get("controllerid");
1762 if (controllerId != null) {
1763 this.controllerId = controllerId;
Ray Milkey269ffb92014-04-03 14:43:30 -07001764 } else {
1765 //Try to get the hostname of the machine and use that for controller ID
1766 try {
1767 String hostname = java.net.InetAddress.getLocalHost().getHostName();
1768 this.controllerId = hostname;
1769 } catch (UnknownHostException e) {
1770 // Can't get hostname, we'll just use the default
1771 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001772 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001773
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001774 log.debug("ControllerId set to {}", this.controllerId);
1775 }
1776
1777 private void initVendorMessages() {
1778 // Configure openflowj to be able to parse the role request/reply
1779 // vendor messages.
1780 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1781 OFNiciraVendorData.NX_VENDOR_ID, 4);
1782 OFVendorId.registerVendorId(niciraVendorId);
1783 OFBasicVendorDataType roleRequestVendorData =
1784 new OFBasicVendorDataType(
1785 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1786 OFRoleRequestVendorData.getInstantiable());
1787 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1788 OFBasicVendorDataType roleReplyVendorData =
1789 new OFBasicVendorDataType(
1790 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1791 OFRoleReplyVendorData.getInstantiable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001792 niciraVendorId.registerVendorDataType(roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001793 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001794
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001795 /**
1796 * Initialize internal data structures
1797 */
1798 public void init(Map<String, String> configParams) {
1799 // These data structures are initialized here because other
1800 // module's startUp() might be called before ours
1801 this.messageListeners =
Ray Milkey269ffb92014-04-03 14:43:30 -07001802 new ConcurrentHashMap<OFType,
1803 ListenerDispatcher<OFType,
1804 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001805 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001806 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1807 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1808 this.controllerNodeIPsCache = new HashMap<String, String>();
1809 this.updates = new LinkedBlockingQueue<IUpdate>();
1810 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001811 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001812 //Set the controller's role to MASTER so it always tries to do role requests.
1813 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001814 this.roleChanger = new RoleChanger();
1815 initVendorMessages();
1816 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001817 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001818
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001819 /**
1820 * Startup all of the controller's components
1821 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001822 @LogMessageDoc(message = "Waiting for storage source",
1823 explanation = "The system database is not yet ready",
1824 recommendation = "If this message persists, this indicates " +
1825 "that the system database has failed to start. " +
1826 LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001827 public void startupComponents() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001828 try {
1829 registryService.registerController(controllerId);
1830 } catch (RegistryException e) {
1831 log.warn("Registry service error: {}", e.getMessage());
1832 }
1833
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001834 // Add our REST API
1835 restApi.addRestletRoutable(new CoreWebRoutable());
1836 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001837
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001838 @Override
1839 public Map<String, String> getControllerNodeIPs() {
1840 // We return a copy of the mapping so we can guarantee that
1841 // the mapping return is the same as one that will be (or was)
1842 // dispatched to IHAListeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001843 HashMap<String, String> retval = new HashMap<String, String>();
1844 synchronized (controllerNodeIPsCache) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001845 retval.putAll(controllerNodeIPsCache);
1846 }
1847 return retval;
1848 }
1849
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001850 @Override
1851 public long getSystemStartTime() {
1852 return (this.systemStartTime);
1853 }
1854
1855 @Override
1856 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1857 this.alwaysClearFlowsOnSwAdd = value;
1858 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001859
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001860 public boolean getAlwaysClearFlowsOnSwAdd() {
1861 return this.alwaysClearFlowsOnSwAdd;
1862 }
1863}