blob: d475fa65bad7e0c741586f10cb7c89c0df4f2c3f [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* 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**/
17
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
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700127 *
128 * Extensions made by ONOS are:
129 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
Jonathan Hart51f6f5b2014-04-03 10:32:10 -0700130 * 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 {
HIGUCHI Yuta0ba6fd02013-06-14 12:46:56 -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
140 private static final String ERROR_DATABASE =
141 "The controller could not communicate with the system database.";
142
143 protected BasicFactory factory;
144 protected ConcurrentMap<OFType,
145 ListenerDispatcher<OFType,IOFMessageListener>>
146 messageListeners;
147 // 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;
157
158 // The controllerNodeIPsCache maps Controller IDs to their IP address.
159 // It's only used by handleControllerNodeIPsChanged
160 protected HashMap<String, String> controllerNodeIPsCache;
161
162 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800163 protected BlockingQueue<IUpdate> updates;
164
165 // 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;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800169
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800170 protected ILinkDiscoveryService linkDiscovery;
171
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";
178
179 // 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;
184
185 // Start time of the controller
186 protected long systemStartTime;
187
188 // Flag to always flush flow table on switch reconnect (HA or otherwise)
189 protected boolean alwaysClearFlowsOnSwAdd = false;
190
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;
Pankaj Berdedc73bb12013-08-14 13:46:38 -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 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700203
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 /**
205 * 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;
212 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
213 this.sw = sw;
214 this.switchUpdateType = switchUpdateType;
215 }
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700216 public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
217 this.sw = sw;
218 this.port = port;
219 this.switchUpdateType = switchUpdateType;
220 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800221 public void dispatch() {
222 if (log.isTraceEnabled()) {
223 log.trace("Dispatching switch update {} {}",
224 sw, switchUpdateType);
225 }
226 if (switchListeners != null) {
227 for (IOFSwitchListener listener : switchListeners) {
228 switch(switchUpdateType) {
229 case ADDED:
230 listener.addedSwitch(sw);
231 break;
232 case REMOVED:
233 listener.removedSwitch(sw);
234 break;
235 case PORTCHANGED:
236 listener.switchPortChanged(sw.getId());
237 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700238 case PORTADDED:
HIGUCHI Yuta36cf0762013-06-14 14:25:38 -0700239 if (listener instanceof IOFSwitchPortListener) {
240 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
241 }
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700242 break;
243 case PORTREMOVED:
HIGUCHI Yuta36cf0762013-06-14 14:25:38 -0700244 if (listener instanceof IOFSwitchPortListener) {
245 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
246 }
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700247 break;
248 default:
249 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800250 }
251 }
252 }
253 }
254 }
255
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800256 // ***************
257 // Getters/Setters
Jonathan Hart20fab1a2013-12-12 11:06:50 -0800258 // ***************
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800259
260 public void setRestApiService(IRestApiService restApi) {
261 this.restApi = restApi;
262 }
263
264 public void setThreadPoolService(IThreadPoolService tp) {
265 this.threadPool = tp;
266 }
267
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800268 public void setMastershipService(IControllerRegistryService serviceImpl) {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800269 this.registryService = serviceImpl;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800270 }
271
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800272 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
273 this.linkDiscovery = linkDiscovery;
274 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800275
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700276 public void publishUpdate(IUpdate update) {
277 try {
278 this.updates.put(update);
279 } catch (InterruptedException e) {
280 log.error("Failure adding update to queue", e);
281 }
282 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800283
284 // **********************
285 // ChannelUpstreamHandler
286 // **********************
287
288 /**
289 * Return a new channel handler for processing a switch connections
290 * @param state The channel state object for the connection
291 * @return the new channel handler
292 */
293 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
294 return new OFChannelHandler(state);
295 }
296
Jonathan Hartcc957a02013-02-26 10:39:04 -0800297 protected class RoleChangeCallback implements ControlChangeCallback {
298 @Override
299 public void controlChanged(long dpid, boolean hasControl) {
300 log.info("Role change callback for switch {}, hasControl {}",
301 HexString.toHexString(dpid), hasControl);
302
303 synchronized(roleChanger){
304 OFSwitchImpl sw = null;
305 for (OFSwitchImpl connectedSw : connectedSwitches){
306 if (connectedSw.getId() == dpid){
307 sw = connectedSw;
308 break;
309 }
310 }
311 if (sw == null){
312 log.warn("Switch {} not found in connected switches",
313 HexString.toHexString(dpid));
314 return;
315 }
316
317 Role role = null;
318
Pankaj Berde01939e92013-03-08 14:38:27 -0800319 /*
320 * issue #229
321 * Cannot rely on sw.getRole() as it can be behind due to pending
322 * role changes in the queue. Just submit it and late the RoleChanger
323 * handle duplicates.
324 */
325
326 if (hasControl){
Jonathan Hartcc957a02013-02-26 10:39:04 -0800327 role = Role.MASTER;
328 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800329 else {
Jonathan Hartcc957a02013-02-26 10:39:04 -0800330 role = Role.SLAVE;
331 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800332
333 log.debug("Sending role request {} msg to {}", role, sw);
334 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
335 swList.add(sw);
336 roleChanger.submitRequest(swList, role);
337
Jonathan Hartcc957a02013-02-26 10:39:04 -0800338 }
339
340 }
341 }
342
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800343 /**
344 * Channel handler deals with the switch connection and dispatches
345 * switch messages to the appropriate locations.
346 * @author readams
347 */
348 protected class OFChannelHandler
349 extends IdleStateAwareChannelUpstreamHandler {
350 protected OFSwitchImpl sw;
351 protected OFChannelState state;
352
353 public OFChannelHandler(OFChannelState state) {
354 this.state = state;
355 }
356
357 @Override
358 @LogMessageDoc(message="New switch connection from {ip address}",
359 explanation="A new switch has connected from the " +
360 "specified IP address")
361 public void channelConnected(ChannelHandlerContext ctx,
362 ChannelStateEvent e) throws Exception {
363 log.info("New switch connection from {}",
364 e.getChannel().getRemoteAddress());
365
366 sw = new OFSwitchImpl();
367 sw.setChannel(e.getChannel());
368 sw.setFloodlightProvider(Controller.this);
369 sw.setThreadPoolService(threadPool);
370
371 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
372 msglist.add(factory.getMessage(OFType.HELLO));
373 e.getChannel().write(msglist);
374
375 }
376
377 @Override
378 @LogMessageDoc(message="Disconnected switch {switch information}",
379 explanation="The specified switch has disconnected.")
380 public void channelDisconnected(ChannelHandlerContext ctx,
381 ChannelStateEvent e) throws Exception {
382 if (sw != null && state.hsState == HandshakeState.READY) {
383 if (activeSwitches.containsKey(sw.getId())) {
384 // It's safe to call removeSwitch even though the map might
385 // not contain this particular switch but another with the
386 // same DPID
387 removeSwitch(sw);
388 }
389 synchronized(roleChanger) {
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700390 if (controlRequested) {
391 registryService.releaseControl(sw.getId());
392 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800393 connectedSwitches.remove(sw);
394 }
395 sw.setConnected(false);
396 }
397 log.info("Disconnected switch {}", sw);
398 }
399
400 @Override
401 @LogMessageDocs({
402 @LogMessageDoc(level="ERROR",
403 message="Disconnecting switch {switch} due to read timeout",
404 explanation="The connected switch has failed to send any " +
405 "messages or respond to echo requests",
406 recommendation=LogMessageDoc.CHECK_SWITCH),
407 @LogMessageDoc(level="ERROR",
408 message="Disconnecting switch {switch}: failed to " +
409 "complete handshake",
410 explanation="The switch did not respond correctly " +
411 "to handshake messages",
412 recommendation=LogMessageDoc.CHECK_SWITCH),
413 @LogMessageDoc(level="ERROR",
414 message="Disconnecting switch {switch} due to IO Error: {}",
415 explanation="There was an error communicating with the switch",
416 recommendation=LogMessageDoc.CHECK_SWITCH),
417 @LogMessageDoc(level="ERROR",
418 message="Disconnecting switch {switch} due to switch " +
419 "state error: {error}",
420 explanation="The switch sent an unexpected message",
421 recommendation=LogMessageDoc.CHECK_SWITCH),
422 @LogMessageDoc(level="ERROR",
423 message="Disconnecting switch {switch} due to " +
424 "message parse failure",
425 explanation="Could not parse a message from the switch",
426 recommendation=LogMessageDoc.CHECK_SWITCH),
427 @LogMessageDoc(level="ERROR",
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800428 message="Could not process message: queue full",
429 explanation="OpenFlow messages are arriving faster than " +
430 " the controller can process them.",
431 recommendation=LogMessageDoc.CHECK_CONTROLLER),
432 @LogMessageDoc(level="ERROR",
433 message="Error while processing message " +
434 "from switch {switch} {cause}",
435 explanation="An error occurred processing the switch message",
436 recommendation=LogMessageDoc.GENERIC_ACTION)
437 })
438 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
439 throws Exception {
440 if (e.getCause() instanceof ReadTimeoutException) {
441 // switch timeout
442 log.error("Disconnecting switch {} due to read timeout", sw);
443 ctx.getChannel().close();
444 } else if (e.getCause() instanceof HandshakeTimeoutException) {
445 log.error("Disconnecting switch {}: failed to complete handshake",
446 sw);
447 ctx.getChannel().close();
448 } else if (e.getCause() instanceof ClosedChannelException) {
449 //log.warn("Channel for sw {} already closed", sw);
450 } else if (e.getCause() instanceof IOException) {
451 log.error("Disconnecting switch {} due to IO Error: {}",
452 sw, e.getCause().getMessage());
453 ctx.getChannel().close();
454 } else if (e.getCause() instanceof SwitchStateException) {
455 log.error("Disconnecting switch {} due to switch state error: {}",
456 sw, e.getCause().getMessage());
457 ctx.getChannel().close();
458 } else if (e.getCause() instanceof MessageParseException) {
459 log.error("Disconnecting switch " + sw +
460 " due to message parse failure",
461 e.getCause());
462 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800463 } else if (e.getCause() instanceof RejectedExecutionException) {
464 log.warn("Could not process message: queue full");
465 } else {
466 log.error("Error while processing message from switch " + sw,
467 e.getCause());
468 ctx.getChannel().close();
469 }
470 }
471
472 @Override
473 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
474 throws Exception {
475 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
476 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
477 e.getChannel().write(msglist);
478 }
479
480 @Override
481 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
482 throws Exception {
483 if (e.getMessage() instanceof List) {
484 @SuppressWarnings("unchecked")
485 List<OFMessage> msglist = (List<OFMessage>)e.getMessage();
486
487 for (OFMessage ofm : msglist) {
488 try {
489 processOFMessage(ofm);
490 }
491 catch (Exception ex) {
492 // We are the last handler in the stream, so run the
493 // exception through the channel again by passing in
494 // ctx.getChannel().
495 Channels.fireExceptionCaught(ctx.getChannel(), ex);
496 }
497 }
498
499 // Flush all flow-mods/packet-out generated from this "train"
500 OFSwitchImpl.flush_all();
501 }
502 }
503
504 /**
505 * Process the request for the switch description
506 */
507 @LogMessageDoc(level="ERROR",
508 message="Exception in reading description " +
509 " during handshake {exception}",
510 explanation="Could not process the switch description string",
511 recommendation=LogMessageDoc.CHECK_SWITCH)
512 void processSwitchDescReply() {
513 try {
514 // Read description, if it has been updated
515 @SuppressWarnings("unchecked")
516 Future<List<OFStatistics>> desc_future =
517 (Future<List<OFStatistics>>)sw.
518 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
519 List<OFStatistics> values =
520 desc_future.get(0, TimeUnit.MILLISECONDS);
521 if (values != null) {
522 OFDescriptionStatistics description =
523 new OFDescriptionStatistics();
524 ChannelBuffer data =
525 ChannelBuffers.buffer(description.getLength());
526 for (OFStatistics f : values) {
527 f.writeTo(data);
528 description.readFrom(data);
529 break; // SHOULD be a list of length 1
530 }
531 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
532 description);
533 sw.setSwitchProperties(description);
534 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800535 }
Jonathan Hart2fa28062013-11-25 20:16:28 -0800536
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800537 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
538 state.hasDescription = true;
539 checkSwitchReady();
540 }
541 catch (InterruptedException ex) {
542 // Ignore
543 }
544 catch (TimeoutException ex) {
545 // Ignore
546 } catch (Exception ex) {
547 log.error("Exception in reading description " +
548 " during handshake", ex);
549 }
550 }
551
552 /**
553 * Send initial switch setup information that we need before adding
554 * the switch
555 * @throws IOException
556 */
557 void sendHelloConfiguration() throws IOException {
558 // Send initial Features Request
Jonathan Hart9e92c512013-03-20 16:24:44 -0700559 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800560 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
561 }
562
563 /**
564 * Send the configuration requests we can only do after we have
565 * the features reply
566 * @throws IOException
567 */
568 void sendFeatureReplyConfiguration() throws IOException {
Jonathan Hart9e92c512013-03-20 16:24:44 -0700569 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800570 // Ensure we receive the full packet via PacketIn
571 OFSetConfig config = (OFSetConfig) factory
572 .getMessage(OFType.SET_CONFIG);
573 config.setMissSendLength((short) 0xffff)
574 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
575 sw.write(config, null);
576 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
577 null);
578
579 // Get Description to set switch-specific flags
580 OFStatisticsRequest req = new OFStatisticsRequest();
581 req.setStatisticType(OFStatisticsType.DESC);
582 req.setLengthU(req.getLengthU());
583 Future<List<OFStatistics>> dfuture =
584 sw.getStatistics(req);
585 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
586 dfuture);
587
588 }
HIGUCHI Yuta0ba6fd02013-06-14 12:46:56 -0700589
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700590 volatile Boolean controlRequested = Boolean.FALSE;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800591 protected void checkSwitchReady() {
592 if (state.hsState == HandshakeState.FEATURES_REPLY &&
593 state.hasDescription && state.hasGetConfigReply) {
594
595 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700596 log.debug("Handshake with {} complete", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800597
598 synchronized(roleChanger) {
599 // We need to keep track of all of the switches that are connected
600 // to the controller, in any role, so that we can later send the
601 // role request messages when the controller role changes.
602 // We need to be synchronized while doing this: we must not
603 // send a another role request to the connectedSwitches until
604 // we were able to add this new switch to connectedSwitches
605 // *and* send the current role to the new switch.
606 connectedSwitches.add(sw);
607
608 if (role != null) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800609 //Put the switch in SLAVE mode until we know we have control
610 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
611 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
612 swList.add(sw);
613 roleChanger.submitRequest(swList, Role.SLAVE);
614
Jonathan Hartcc957a02013-02-26 10:39:04 -0800615 //Request control of the switch from the global registry
616 try {
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700617 controlRequested = Boolean.TRUE;
Jonathan Hartcc957a02013-02-26 10:39:04 -0800618 registryService.requestControl(sw.getId(),
619 new RoleChangeCallback());
620 } catch (RegistryException e) {
621 log.debug("Registry error: {}", e.getMessage());
Pankaj Berde99fcee12013-03-18 09:41:53 -0700622 controlRequested = Boolean.FALSE;
Jonathan Hartcc957a02013-02-26 10:39:04 -0800623 }
624
Jonathan Hart97801ac2013-02-26 14:29:16 -0800625
Jonathan Hartcc957a02013-02-26 10:39:04 -0800626
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800627 // Send a role request if role support is enabled for the controller
628 // This is a probe that we'll use to determine if the switch
629 // actually supports the role request message. If it does we'll
630 // get back a role reply message. If it doesn't we'll get back an
631 // OFError message.
632 // If role is MASTER we will promote switch to active
633 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800634 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800635 log.debug("This controller's role is {}, " +
636 "sending initial role request msg to {}",
637 role, sw);
638 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
639 swList.add(sw);
640 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800641 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800642 }
643 else {
644 // Role supported not enabled on controller (for now)
645 // automatically promote switch to active state.
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800646 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800647 "not sending role request msg to {}",
648 role, sw);
649 // Need to clear FlowMods before we add the switch
650 // and dispatch updates otherwise we have a race condition.
651 sw.clearAllFlowMods();
652 addSwitch(sw);
653 state.firstRoleReplyReceived = true;
654 }
655 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700656 if (!controlRequested) {
657 // yield to allow other thread(s) to release control
658 try {
659 Thread.sleep(10);
660 } catch (InterruptedException e) {
661 // Ignore interruptions
662 }
663 // safer to bounce the switch to reconnect here than proceeding further
Jonathan Hart9e92c512013-03-20 16:24:44 -0700664 log.debug("Closing {} because we weren't able to request control " +
665 "successfully" + sw);
Pankaj Berde99fcee12013-03-18 09:41:53 -0700666 sw.channel.close();
667 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800668 }
669 }
670
671 /* Handle a role reply message we received from the switch. Since
672 * netty serializes message dispatch we don't need to synchronize
673 * against other receive operations from the same switch, so no need
674 * to synchronize addSwitch(), removeSwitch() operations from the same
675 * connection.
676 * FIXME: However, when a switch with the same DPID connects we do
677 * need some synchronization. However, handling switches with same
678 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
679 * removedSwitch notification):1
680 *
681 */
682 @LogMessageDoc(level="ERROR",
683 message="Invalid role value in role reply message",
684 explanation="Was unable to set the HA role (master or slave) " +
685 "for the controller.",
686 recommendation=LogMessageDoc.CHECK_CONTROLLER)
687 protected void handleRoleReplyMessage(OFVendor vendorMessage,
688 OFRoleReplyVendorData roleReplyVendorData) {
689 // Map from the role code in the message to our role enum
690 int nxRole = roleReplyVendorData.getRole();
691 Role role = null;
692 switch (nxRole) {
693 case OFRoleVendorData.NX_ROLE_OTHER:
694 role = Role.EQUAL;
695 break;
696 case OFRoleVendorData.NX_ROLE_MASTER:
697 role = Role.MASTER;
698 break;
699 case OFRoleVendorData.NX_ROLE_SLAVE:
700 role = Role.SLAVE;
701 break;
702 default:
703 log.error("Invalid role value in role reply message");
704 sw.getChannel().close();
705 return;
706 }
707
708 log.debug("Handling role reply for role {} from {}. " +
709 "Controller's role is {} ",
710 new Object[] { role, sw, Controller.this.role}
711 );
712
713 sw.deliverRoleReply(vendorMessage.getXid(), role);
714
715 boolean isActive = activeSwitches.containsKey(sw.getId());
716 if (!isActive && sw.isActive()) {
717 // Transition from SLAVE to MASTER.
718
719 if (!state.firstRoleReplyReceived ||
720 getAlwaysClearFlowsOnSwAdd()) {
721 // This is the first role-reply message we receive from
722 // this switch or roles were disabled when the switch
723 // connected:
724 // Delete all pre-existing flows for new connections to
725 // the master
726 //
727 // FIXME: Need to think more about what the test should
728 // be for when we flush the flow-table? For example,
729 // if all the controllers are temporarily in the backup
730 // role (e.g. right after a failure of the master
731 // controller) at the point the switch connects, then
732 // all of the controllers will initially connect as
733 // backup controllers and not flush the flow-table.
734 // Then when one of them is promoted to master following
735 // the master controller election the flow-table
736 // will still not be flushed because that's treated as
737 // a failover event where we don't want to flush the
738 // flow-table. The end result would be that the flow
739 // table for a newly connected switch is never
740 // flushed. Not sure how to handle that case though...
741 sw.clearAllFlowMods();
742 log.debug("First role reply from master switch {}, " +
743 "clear FlowTable to active switch list",
744 HexString.toHexString(sw.getId()));
745 }
746
747 // Some switches don't seem to update us with port
748 // status messages while in slave role.
Jonathan Hart2fa28062013-11-25 20:16:28 -0800749 //readSwitchPortStateFromStorage(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800750
751 // Only add the switch to the active switch list if
752 // we're not in the slave role. Note that if the role
753 // attribute is null, then that means that the switch
754 // doesn't support the role request messages, so in that
755 // case we're effectively in the EQUAL role and the
756 // switch should be included in the active switch list.
757 addSwitch(sw);
758 log.debug("Added master switch {} to active switch list",
759 HexString.toHexString(sw.getId()));
760
761 }
762 else if (isActive && !sw.isActive()) {
763 // Transition from MASTER to SLAVE: remove switch
764 // from active switch list.
765 log.debug("Removed slave switch {} from active switch" +
766 " list", HexString.toHexString(sw.getId()));
767 removeSwitch(sw);
768 }
769
770 // Indicate that we have received a role reply message.
771 state.firstRoleReplyReceived = true;
772 }
773
774 protected boolean handleVendorMessage(OFVendor vendorMessage) {
775 boolean shouldHandleMessage = false;
776 int vendor = vendorMessage.getVendor();
777 switch (vendor) {
778 case OFNiciraVendorData.NX_VENDOR_ID:
779 OFNiciraVendorData niciraVendorData =
780 (OFNiciraVendorData)vendorMessage.getVendorData();
781 int dataType = niciraVendorData.getDataType();
782 switch (dataType) {
783 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
784 OFRoleReplyVendorData roleReplyVendorData =
785 (OFRoleReplyVendorData) niciraVendorData;
786 handleRoleReplyMessage(vendorMessage,
787 roleReplyVendorData);
788 break;
789 default:
790 log.warn("Unhandled Nicira VENDOR message; " +
791 "data type = {}", dataType);
792 break;
793 }
794 break;
795 default:
796 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
797 break;
798 }
799
800 return shouldHandleMessage;
801 }
802
803 /**
804 * Dispatch an Openflow message from a switch to the appropriate
805 * handler.
806 * @param m The message to process
807 * @throws IOException
808 * @throws SwitchStateException
809 */
810 @LogMessageDocs({
811 @LogMessageDoc(level="WARN",
812 message="Config Reply from {switch} has " +
813 "miss length set to {length}",
814 explanation="The controller requires that the switch " +
815 "use a miss length of 0xffff for correct " +
816 "function",
817 recommendation="Use a different switch to ensure " +
818 "correct function"),
819 @LogMessageDoc(level="WARN",
820 message="Received ERROR from sw {switch} that "
821 +"indicates roles are not supported "
822 +"but we have received a valid "
823 +"role reply earlier",
824 explanation="The switch sent a confusing message to the" +
825 "controller")
826 })
827 protected void processOFMessage(OFMessage m)
828 throws IOException, SwitchStateException {
829 boolean shouldHandleMessage = false;
830
831 switch (m.getType()) {
832 case HELLO:
833 if (log.isTraceEnabled())
834 log.trace("HELLO from {}", sw);
835
836 if (state.hsState.equals(HandshakeState.START)) {
837 state.hsState = HandshakeState.HELLO;
838 sendHelloConfiguration();
839 } else {
840 throw new SwitchStateException("Unexpected HELLO from "
841 + sw);
842 }
843 break;
844 case ECHO_REQUEST:
845 OFEchoReply reply =
846 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
847 reply.setXid(m.getXid());
848 sw.write(reply, null);
849 break;
850 case ECHO_REPLY:
851 break;
852 case FEATURES_REPLY:
853 if (log.isTraceEnabled())
854 log.trace("Features Reply from {}", sw);
855
856 sw.setFeaturesReply((OFFeaturesReply) m);
857 if (state.hsState.equals(HandshakeState.HELLO)) {
858 sendFeatureReplyConfiguration();
859 state.hsState = HandshakeState.FEATURES_REPLY;
860 // uncomment to enable "dumb" switches like cbench
861 // state.hsState = HandshakeState.READY;
862 // addSwitch(sw);
863 } else {
864 // return results to rest api caller
865 sw.deliverOFFeaturesReply(m);
866 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800867 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800868 }
869 break;
870 case GET_CONFIG_REPLY:
871 if (log.isTraceEnabled())
872 log.trace("Get config reply from {}", sw);
873
874 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
875 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
876 throw new SwitchStateException(em);
877 }
878 OFGetConfigReply cr = (OFGetConfigReply) m;
879 if (cr.getMissSendLength() == (short)0xffff) {
880 log.trace("Config Reply from {} confirms " +
881 "miss length set to 0xffff", sw);
882 } else {
883 log.warn("Config Reply from {} has " +
884 "miss length set to {}",
885 sw, cr.getMissSendLength() & 0xffff);
886 }
887 state.hasGetConfigReply = true;
888 checkSwitchReady();
889 break;
890 case VENDOR:
891 shouldHandleMessage = handleVendorMessage((OFVendor)m);
892 break;
893 case ERROR:
Jonathan Hart3525df92013-03-19 14:09:13 -0700894 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800895 // TODO: we need better error handling. Especially for
896 // request/reply style message (stats, roles) we should have
897 // a unified way to lookup the xid in the error message.
898 // This will probable involve rewriting the way we handle
899 // request/reply style messages.
900 OFError error = (OFError) m;
901 boolean shouldLogError = true;
902 // TODO: should we check that firstRoleReplyReceived is false,
903 // i.e., check only whether the first request fails?
904 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
905 boolean isBadVendorError =
906 (error.getErrorType() == OFError.OFErrorType.
907 OFPET_BAD_REQUEST.getValue());
908 // We expect to receive a bad vendor error when
909 // we're connected to a switch that doesn't support
910 // the Nicira vendor extensions (i.e. not OVS or
911 // derived from OVS). By protocol, it should also be
912 // BAD_VENDOR, but too many switch implementations
913 // get it wrong and we can already check the xid()
914 // so we can ignore the type with confidence that this
915 // is not a spurious error
916 shouldLogError = !isBadVendorError;
917 if (isBadVendorError) {
Jonathan Hart3525df92013-03-19 14:09:13 -0700918 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800919 if (state.firstRoleReplyReceived && (role != null)) {
920 log.warn("Received ERROR from sw {} that "
921 +"indicates roles are not supported "
922 +"but we have received a valid "
923 +"role reply earlier", sw);
924 }
925 state.firstRoleReplyReceived = true;
Jonathan Harta95c6d92013-03-18 16:12:27 -0700926 Role requestedRole =
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700927 sw.deliverRoleRequestNotSupportedEx(error.getXid());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800928 synchronized(roleChanger) {
929 if (sw.role == null && Controller.this.role==Role.SLAVE) {
Jonathan Harta95c6d92013-03-18 16:12:27 -0700930 //This will now never happen. The Controller's role
931 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800932 // the switch doesn't understand role request
933 // messages and the current controller role is
934 // slave. We need to disconnect the switch.
935 // @see RoleChanger for rationale
Jonathan Hart9e92c512013-03-20 16:24:44 -0700936 log.warn("Closing {} channel because controller's role " +
937 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800938 sw.getChannel().close();
939 }
Jonathan Harta95c6d92013-03-18 16:12:27 -0700940 else if (sw.role == null && requestedRole == Role.MASTER) {
Jonathan Hart3525df92013-03-19 14:09:13 -0700941 log.debug("Adding switch {} because we got an error" +
942 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800943 // Controller's role is master: add to
944 // active
945 // TODO: check if clearing flow table is
946 // right choice here.
947 // Need to clear FlowMods before we add the switch
948 // and dispatch updates otherwise we have a race condition.
949 // TODO: switch update is async. Won't we still have a potential
950 // race condition?
951 sw.clearAllFlowMods();
952 addSwitch(sw);
953 }
954 }
955 }
956 else {
957 // 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.
Jonathan Hart9e92c512013-03-20 16:24:44 -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:
987 if (state.hsState.ordinal() <
988 HandshakeState.FEATURES_REPLY.ordinal()) {
989 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.
1002 equals(HandshakeState.READY) &&
1003 (sw.getRole() != Role.SLAVE);
1004 handlePortStatusMessage(sw, (OFPortStatus)m, updateStorage);
1005 shouldHandleMessage = true;
1006 break;
1007
1008 default:
1009 shouldHandleMessage = true;
1010 break;
1011 }
1012
1013 if (shouldHandleMessage) {
1014 sw.getListenerReadLock().lock();
1015 try {
1016 if (sw.isConnected()) {
1017 if (!state.hsState.equals(HandshakeState.READY)) {
1018 log.debug("Ignoring message type {} received " +
1019 "from switch {} before switch is " +
1020 "fully configured.", m.getType(), sw);
1021 }
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 }
1046 }
1047 finally {
1048 sw.getListenerReadLock().unlock();
1049 }
1050 }
1051 }
1052 }
1053
1054 // ****************
1055 // Message handlers
1056 // ****************
1057
1058 protected void handlePortStatusMessage(IOFSwitch sw,
1059 OFPortStatus m,
1060 boolean updateStorage) {
1061 short portNumber = m.getDesc().getPortNumber();
1062 OFPhysicalPort port = m.getDesc();
1063 if (m.getReason() == (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
Pankaj Berde6a4075d2013-01-22 16:42:54 -08001064 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1065 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001066 sw.setPort(port);
Pankaj Berde6a4075d2013-01-22 16:42:54 -08001067 if (!portDown) {
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001068 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1069 try {
1070 this.updates.put(update);
1071 } catch (InterruptedException e) {
1072 log.error("Failure adding update to queue", e);
1073 }
Pankaj Berde6debb042013-01-16 18:04:32 -08001074 } else {
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001075 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1076 try {
1077 this.updates.put(update);
1078 } catch (InterruptedException e) {
1079 log.error("Failure adding update to queue", e);
1080 }
Pankaj Berde6debb042013-01-16 18:04:32 -08001081 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001082 //if (updateStorage)
1083 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001084 log.debug("Port #{} modified for {}", portNumber, sw);
1085 } else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) {
Jonathan Hart8a5d0972013-12-04 10:02:44 -08001086 // XXX Workaround to prevent race condition where a link is detected
1087 // and attempted to be written to the database before the port is in
1088 // the database. We now suppress link discovery on ports until we're
1089 // sure they're in the database.
1090 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1091
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001092 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001093 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1094 try {
1095 this.updates.put(update);
1096 } catch (InterruptedException e) {
1097 log.error("Failure adding update to queue", e);
1098 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001099 //if (updateStorage)
1100 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001101 log.debug("Port #{} added for {}", portNumber, sw);
1102 } else if (m.getReason() ==
1103 (byte)OFPortReason.OFPPR_DELETE.ordinal()) {
1104 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001105 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1106 try {
1107 this.updates.put(update);
1108 } catch (InterruptedException e) {
1109 log.error("Failure adding update to queue", e);
1110 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001111 //if (updateStorage)
1112 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001113 log.debug("Port #{} deleted for {}", portNumber, sw);
1114 }
1115 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1116 try {
1117 this.updates.put(update);
1118 } catch (InterruptedException e) {
1119 log.error("Failure adding update to queue", e);
1120 }
1121 }
1122
1123 /**
1124 * flcontext_cache - Keep a thread local stack of contexts
1125 */
1126 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
1127 new ThreadLocal <Stack<FloodlightContext>> () {
1128 @Override
1129 protected Stack<FloodlightContext> initialValue() {
1130 return new Stack<FloodlightContext>();
1131 }
1132 };
1133
1134 /**
1135 * flcontext_alloc - pop a context off the stack, if required create a new one
1136 * @return FloodlightContext
1137 */
1138 protected static FloodlightContext flcontext_alloc() {
1139 FloodlightContext flcontext = null;
1140
1141 if (flcontext_cache.get().empty()) {
1142 flcontext = new FloodlightContext();
1143 }
1144 else {
1145 flcontext = flcontext_cache.get().pop();
1146 }
1147
1148 return flcontext;
1149 }
1150
1151 /**
1152 * flcontext_free - Free the context to the current thread
1153 * @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
1162 * @param sw The switch for the message
1163 * @param m The message
1164 * @param bContext The floodlight context. If null then floodlight context would
1165 * be allocated in this function
1166 * @throws IOException
1167 */
1168 @LogMessageDocs({
1169 @LogMessageDoc(level="ERROR",
1170 message="Ignoring PacketIn (Xid = {xid}) because the data" +
1171 " field is empty.",
1172 explanation="The switch sent an improperly-formatted PacketIn" +
1173 " message",
1174 recommendation=LogMessageDoc.CHECK_SWITCH),
1175 @LogMessageDoc(level="WARN",
1176 message="Unhandled OF Message: {} from {}",
1177 explanation="The switch sent a message not handled by " +
1178 "the controller")
1179 })
1180 protected void handleMessage(IOFSwitch sw, OFMessage m,
1181 FloodlightContext bContext)
1182 throws IOException {
1183 Ethernet eth = null;
1184
1185 switch (m.getType()) {
1186 case PACKET_IN:
1187 OFPacketIn pi = (OFPacketIn)m;
1188
1189 if (pi.getPacketData().length <= 0) {
1190 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1191 ") because the data field is empty.");
1192 return;
1193 }
1194
1195 if (Controller.ALWAYS_DECODE_ETH) {
1196 eth = new Ethernet();
1197 eth.deserialize(pi.getPacketData(), 0,
1198 pi.getPacketData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001199 }
1200 // fall through to default case...
1201
1202 default:
1203
1204 List<IOFMessageListener> listeners = null;
1205 if (messageListeners.containsKey(m.getType())) {
1206 listeners = messageListeners.get(m.getType()).
1207 getOrderedListeners();
1208 }
1209
1210 FloodlightContext bc = null;
1211 if (listeners != null) {
1212 // Check if floodlight context is passed from the calling
1213 // function, if so use that floodlight context, otherwise
1214 // allocate one
1215 if (bContext == null) {
1216 bc = flcontext_alloc();
1217 } else {
1218 bc = bContext;
1219 }
1220 if (eth != null) {
1221 IFloodlightProviderService.bcStore.put(bc,
1222 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
1223 eth);
1224 }
1225
1226 // Get the starting time (overall and per-component) of
1227 // the processing chain for this packet if performance
1228 // monitoring is turned on
mininet73e7fb72013-12-03 14:25:53 -08001229
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001230 Command cmd;
1231 for (IOFMessageListener listener : listeners) {
1232 if (listener instanceof IOFSwitchFilter) {
1233 if (!((IOFSwitchFilter)listener).isInterested(sw)) {
1234 continue;
1235 }
1236 }
1237
mininet73e7fb72013-12-03 14:25:53 -08001238
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001239 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001240
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001241
1242 if (Command.STOP.equals(cmd)) {
1243 break;
1244 }
1245 }
mininet73e7fb72013-12-03 14:25:53 -08001246
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001247 } else {
1248 log.warn("Unhandled OF Message: {} from {}", m, sw);
1249 }
1250
1251 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1252 }
1253 }
1254
1255 /**
1256 * Log an OpenFlow error message from a switch
1257 * @param sw The switch that sent the error
1258 * @param error The error message
1259 */
1260 @LogMessageDoc(level="ERROR",
1261 message="Error {error type} {error code} from {switch}",
1262 explanation="The switch responded with an unexpected error" +
1263 "to an OpenFlow message from the controller",
1264 recommendation="This could indicate improper network operation. " +
1265 "If the problem persists restarting the switch and " +
1266 "controller may help."
1267 )
1268 protected void logError(IOFSwitch sw, OFError error) {
1269 int etint = 0xffff & error.getErrorType();
1270 if (etint < 0 || etint >= OFErrorType.values().length) {
1271 log.error("Unknown error code {} from sw {}", etint, sw);
1272 }
1273 OFErrorType et = OFErrorType.values()[etint];
1274 switch (et) {
1275 case OFPET_HELLO_FAILED:
1276 OFHelloFailedCode hfc =
1277 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1278 log.error("Error {} {} from {}", new Object[] {et, hfc, sw});
1279 break;
1280 case OFPET_BAD_REQUEST:
1281 OFBadRequestCode brc =
1282 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1283 log.error("Error {} {} from {}", new Object[] {et, brc, sw});
1284 break;
1285 case OFPET_BAD_ACTION:
1286 OFBadActionCode bac =
1287 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1288 log.error("Error {} {} from {}", new Object[] {et, bac, sw});
1289 break;
1290 case OFPET_FLOW_MOD_FAILED:
1291 OFFlowModFailedCode fmfc =
1292 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1293 log.error("Error {} {} from {}", new Object[] {et, fmfc, sw});
1294 break;
1295 case OFPET_PORT_MOD_FAILED:
1296 OFPortModFailedCode pmfc =
1297 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1298 log.error("Error {} {} from {}", new Object[] {et, pmfc, sw});
1299 break;
1300 case OFPET_QUEUE_OP_FAILED:
1301 OFQueueOpFailedCode qofc =
1302 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1303 log.error("Error {} {} from {}", new Object[] {et, qofc, sw});
1304 break;
1305 default:
1306 break;
1307 }
1308 }
1309
1310 /**
1311 * Add a switch to the active switch list and call the switch listeners.
1312 * This happens either when a switch first connects (and the controller is
1313 * not in the slave role) or when the role of the controller changes from
1314 * slave to master.
1315 * @param sw the switch that has been added
1316 */
1317 // TODO: need to rethink locking and the synchronous switch update.
1318 // We can / should also handle duplicate DPIDs in connectedSwitches
1319 @LogMessageDoc(level="ERROR",
1320 message="New switch added {switch} for already-added switch {switch}",
1321 explanation="A switch with the same DPID as another switch " +
1322 "connected to the controller. This can be caused by " +
1323 "multiple switches configured with the same DPID, or " +
1324 "by a switch reconnected very quickly after " +
1325 "disconnecting.",
1326 recommendation="If this happens repeatedly, it is likely there " +
1327 "are switches with duplicate DPIDs on the network. " +
1328 "Reconfigure the appropriate switches. If it happens " +
1329 "very rarely, then it is likely this is a transient " +
1330 "network problem that can be ignored."
1331 )
1332 protected void addSwitch(IOFSwitch sw) {
Jonathan Hart8a5d0972013-12-04 10:02:44 -08001333 // XXX Workaround to prevent race condition where a link is detected
1334 // and attempted to be written to the database before the port is in
1335 // the database. We now suppress link discovery on ports until we're
1336 // sure they're in the database.
1337 for (OFPhysicalPort port : sw.getPorts()) {
1338 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1339 }
1340
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001341 // TODO: is it safe to modify the HashMap without holding
1342 // the old switch's lock?
1343 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1344 if (sw == oldSw) {
1345 // Note == for object equality, not .equals for value
1346 log.info("New add switch for pre-existing switch {}", sw);
1347 return;
1348 }
1349
1350 if (oldSw != null) {
1351 oldSw.getListenerWriteLock().lock();
1352 try {
1353 log.error("New switch added {} for already-added switch {}",
1354 sw, oldSw);
1355 // Set the connected flag to false to suppress calling
1356 // the listeners for this switch in processOFMessage
1357 oldSw.setConnected(false);
1358
1359 oldSw.cancelAllStatisticsReplies();
1360
Jonathan Hart2fa28062013-11-25 20:16:28 -08001361 //updateInactiveSwitchInfo(oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001362
1363 // we need to clean out old switch state definitively
1364 // before adding the new switch
1365 // FIXME: It seems not completely kosher to call the
1366 // switch listeners here. I thought one of the points of
1367 // having the asynchronous switch update mechanism was so
1368 // the addedSwitch and removedSwitch were always called
1369 // from a single thread to simplify concurrency issues
1370 // for the listener.
1371 if (switchListeners != null) {
1372 for (IOFSwitchListener listener : switchListeners) {
1373 listener.removedSwitch(oldSw);
1374 }
1375 }
1376 // will eventually trigger a removeSwitch(), which will cause
1377 // a "Not removing Switch ... already removed debug message.
1378 // TODO: Figure out a way to handle this that avoids the
1379 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001380 log.debug("Closing {} because a new IOFSwitch got added " +
1381 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001382 oldSw.getChannel().close();
1383 }
1384 finally {
1385 oldSw.getListenerWriteLock().unlock();
1386 }
1387 }
1388
Jonathan Hart2fa28062013-11-25 20:16:28 -08001389 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001390 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1391 try {
1392 this.updates.put(update);
1393 } catch (InterruptedException e) {
1394 log.error("Failure adding update to queue", e);
1395 }
1396 }
1397
1398 /**
1399 * Remove a switch from the active switch list and call the switch listeners.
1400 * This happens either when the switch is disconnected or when the
1401 * controller's role for the switch changes from master to slave.
1402 * @param sw the switch that has been removed
1403 */
1404 protected void removeSwitch(IOFSwitch sw) {
1405 // No need to acquire the listener lock, since
1406 // this method is only called after netty has processed all
1407 // pending messages
1408 log.debug("removeSwitch: {}", sw);
1409 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1410 log.debug("Not removing switch {}; already removed", sw);
1411 return;
1412 }
1413 // We cancel all outstanding statistics replies if the switch transition
1414 // from active. In the future we might allow statistics requests
1415 // from slave controllers. Then we need to move this cancelation
1416 // to switch disconnect
1417 sw.cancelAllStatisticsReplies();
1418
1419 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1420 // here if role support is enabled. In that case if the switch is being
1421 // removed because we've been switched to being in the slave role, then I think
1422 // it's possible that the new master may have already been promoted to master
1423 // and written out the active switch state to storage. If we now execute
1424 // updateInactiveSwitchInfo we may wipe out all of the state that was
1425 // written out by the new master. Maybe need to revisit how we handle all
1426 // of the switch state that's written to storage.
1427
Jonathan Hart2fa28062013-11-25 20:16:28 -08001428 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001429 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1430 try {
1431 this.updates.put(update);
1432 } catch (InterruptedException e) {
1433 log.error("Failure adding update to queue", e);
1434 }
1435 }
1436
1437 // ***************
1438 // IFloodlightProvider
1439 // ***************
1440
1441 @Override
1442 public synchronized void addOFMessageListener(OFType type,
1443 IOFMessageListener listener) {
1444 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1445 messageListeners.get(type);
1446 if (ldd == null) {
1447 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1448 messageListeners.put(type, ldd);
1449 }
1450 ldd.addListener(type, listener);
1451 }
1452
1453 @Override
1454 public synchronized void removeOFMessageListener(OFType type,
1455 IOFMessageListener listener) {
1456 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1457 messageListeners.get(type);
1458 if (ldd != null) {
1459 ldd.removeListener(listener);
1460 }
1461 }
1462
1463 private void logListeners() {
1464 for (Map.Entry<OFType,
1465 ListenerDispatcher<OFType,
1466 IOFMessageListener>> entry
1467 : messageListeners.entrySet()) {
1468
1469 OFType type = entry.getKey();
1470 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1471 entry.getValue();
1472
1473 StringBuffer sb = new StringBuffer();
1474 sb.append("OFListeners for ");
1475 sb.append(type);
1476 sb.append(": ");
1477 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1478 sb.append(l.getName());
1479 sb.append(",");
1480 }
1481 log.debug(sb.toString());
1482 }
1483 }
1484
1485 public void removeOFMessageListeners(OFType type) {
1486 messageListeners.remove(type);
1487 }
1488
1489 @Override
1490 public Map<Long, IOFSwitch> getSwitches() {
1491 return Collections.unmodifiableMap(this.activeSwitches);
1492 }
1493
1494 @Override
1495 public void addOFSwitchListener(IOFSwitchListener listener) {
1496 this.switchListeners.add(listener);
1497 }
1498
1499 @Override
1500 public void removeOFSwitchListener(IOFSwitchListener listener) {
1501 this.switchListeners.remove(listener);
1502 }
1503
1504 @Override
1505 public Map<OFType, List<IOFMessageListener>> getListeners() {
1506 Map<OFType, List<IOFMessageListener>> lers =
1507 new HashMap<OFType, List<IOFMessageListener>>();
1508 for(Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1509 messageListeners.entrySet()) {
1510 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1511 }
1512 return Collections.unmodifiableMap(lers);
1513 }
1514
1515 @Override
1516 @LogMessageDocs({
1517 @LogMessageDoc(message="Failed to inject OFMessage {message} onto " +
1518 "a null switch",
1519 explanation="Failed to process a message because the switch " +
1520 " is no longer connected."),
1521 @LogMessageDoc(level="ERROR",
1522 message="Error reinjecting OFMessage on switch {switch}",
1523 explanation="An I/O error occured while attempting to " +
1524 "process an OpenFlow message",
1525 recommendation=LogMessageDoc.CHECK_SWITCH)
1526 })
1527 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1528 FloodlightContext bc) {
1529 if (sw == null) {
1530 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1531 return false;
1532 }
1533
1534 // FIXME: Do we need to be able to inject messages to switches
1535 // where we're the slave controller (i.e. they're connected but
1536 // not active)?
1537 // FIXME: Don't we need synchronization logic here so we're holding
1538 // the listener read lock when we call handleMessage? After some
1539 // discussions it sounds like the right thing to do here would be to
1540 // inject the message as a netty upstream channel event so it goes
1541 // through the normal netty event processing, including being
1542 // handled
1543 if (!activeSwitches.containsKey(sw.getId())) return false;
1544
1545 try {
1546 // Pass Floodlight context to the handleMessages()
1547 handleMessage(sw, msg, bc);
1548 } catch (IOException e) {
1549 log.error("Error reinjecting OFMessage on switch {}",
1550 HexString.toHexString(sw.getId()));
1551 return false;
1552 }
1553 return true;
1554 }
1555
1556 @Override
1557 @LogMessageDoc(message="Calling System.exit",
1558 explanation="The controller is terminating")
1559 public synchronized void terminate() {
1560 log.info("Calling System.exit");
1561 System.exit(1);
1562 }
1563
1564 @Override
1565 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1566 // call the overloaded version with floodlight context set to null
1567 return injectOfMessage(sw, msg, null);
1568 }
1569
1570 @Override
1571 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1572 FloodlightContext bc) {
1573 if (log.isTraceEnabled()) {
1574 String str = OFMessage.getDataAsString(sw, m, bc);
1575 log.trace("{}", str);
1576 }
1577
1578 List<IOFMessageListener> listeners = null;
1579 if (messageListeners.containsKey(m.getType())) {
1580 listeners =
1581 messageListeners.get(m.getType()).getOrderedListeners();
1582 }
1583
1584 if (listeners != null) {
1585 for (IOFMessageListener listener : listeners) {
1586 if (listener instanceof IOFSwitchFilter) {
1587 if (!((IOFSwitchFilter)listener).isInterested(sw)) {
1588 continue;
1589 }
1590 }
1591 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1592 break;
1593 }
1594 }
1595 }
1596 }
1597
1598 @Override
1599 public BasicFactory getOFMessageFactory() {
1600 return factory;
1601 }
1602
1603 @Override
1604 public String getControllerId() {
1605 return controllerId;
1606 }
1607
1608 // **************
1609 // Initialization
1610 // **************
1611
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001612 /**
1613 * Sets the initial role based on properties in the config params.
1614 * It looks for two different properties.
1615 * If the "role" property is specified then the value should be
1616 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1617 * controller is set to the specified value. If the "role" property
1618 * is not specified then it looks next for the "role.path" property.
1619 * In this case the value should be the path to a property file in
1620 * the file system that contains a property called "floodlight.role"
1621 * which can be one of the values listed above for the "role" property.
1622 * The idea behind the "role.path" mechanism is that you have some
1623 * separate heartbeat and master controller election algorithm that
1624 * determines the role of the controller. When a role transition happens,
1625 * it updates the current role in the file specified by the "role.path"
1626 * file. Then if floodlight restarts for some reason it can get the
1627 * correct current role of the controller from the file.
1628 * @param configParams The config params for the FloodlightProvider service
1629 * @return A valid role if role information is specified in the
1630 * config params, otherwise null
1631 */
1632 @LogMessageDocs({
1633 @LogMessageDoc(message="Controller role set to {role}",
1634 explanation="Setting the initial HA role to "),
1635 @LogMessageDoc(level="ERROR",
1636 message="Invalid current role value: {role}",
1637 explanation="An invalid HA role value was read from the " +
1638 "properties file",
1639 recommendation=LogMessageDoc.CHECK_CONTROLLER)
1640 })
1641 protected Role getInitialRole(Map<String, String> configParams) {
1642 Role role = null;
1643 String roleString = configParams.get("role");
1644 if (roleString == null) {
1645 String rolePath = configParams.get("rolepath");
1646 if (rolePath != null) {
1647 Properties properties = new Properties();
1648 try {
1649 properties.load(new FileInputStream(rolePath));
1650 roleString = properties.getProperty("floodlight.role");
1651 }
1652 catch (IOException exc) {
1653 // Don't treat it as an error if the file specified by the
1654 // rolepath property doesn't exist. This lets us enable the
1655 // HA mechanism by just creating/setting the floodlight.role
1656 // property in that file without having to modify the
1657 // floodlight properties.
1658 }
1659 }
1660 }
1661
1662 if (roleString != null) {
1663 // Canonicalize the string to the form used for the enum constants
1664 roleString = roleString.trim().toUpperCase();
1665 try {
1666 role = Role.valueOf(roleString);
1667 }
1668 catch (IllegalArgumentException exc) {
1669 log.error("Invalid current role value: {}", roleString);
1670 }
1671 }
1672
1673 log.info("Controller role set to {}", role);
1674
1675 return role;
1676 }
1677
1678 /**
1679 * Tell controller that we're ready to accept switches loop
1680 * @throws IOException
1681 */
1682 @LogMessageDocs({
1683 @LogMessageDoc(message="Listening for switch connections on {address}",
1684 explanation="The controller is ready and listening for new" +
1685 " switch connections"),
1686 @LogMessageDoc(message="Storage exception in controller " +
1687 "updates loop; terminating process",
1688 explanation=ERROR_DATABASE,
1689 recommendation=LogMessageDoc.CHECK_CONTROLLER),
1690 @LogMessageDoc(level="ERROR",
1691 message="Exception in controller updates loop",
1692 explanation="Failed to dispatch controller event",
1693 recommendation=LogMessageDoc.GENERIC_ACTION)
1694 })
1695 public void run() {
1696 if (log.isDebugEnabled()) {
1697 logListeners();
1698 }
1699
1700 try {
1701 final ServerBootstrap bootstrap = createServerBootStrap();
1702
1703 bootstrap.setOption("reuseAddr", true);
1704 bootstrap.setOption("child.keepAlive", true);
1705 bootstrap.setOption("child.tcpNoDelay", true);
1706 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1707
1708 ChannelPipelineFactory pfact =
1709 new OpenflowPipelineFactory(this, null);
1710 bootstrap.setPipelineFactory(pfact);
1711 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1712 final ChannelGroup cg = new DefaultChannelGroup();
1713 cg.add(bootstrap.bind(sa));
1714
1715 log.info("Listening for switch connections on {}", sa);
1716 } catch (Exception e) {
1717 throw new RuntimeException(e);
1718 }
1719
1720 // main loop
1721 while (true) {
1722 try {
1723 IUpdate update = updates.take();
1724 update.dispatch();
1725 } catch (InterruptedException e) {
1726 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001727 } catch (Exception e) {
1728 log.error("Exception in controller updates loop", e);
1729 }
1730 }
1731 }
1732
1733 private ServerBootstrap createServerBootStrap() {
1734 if (workerThreads == 0) {
1735 return new ServerBootstrap(
1736 new NioServerSocketChannelFactory(
1737 Executors.newCachedThreadPool(),
1738 Executors.newCachedThreadPool()));
1739 } else {
1740 return new ServerBootstrap(
1741 new NioServerSocketChannelFactory(
1742 Executors.newCachedThreadPool(),
1743 Executors.newCachedThreadPool(), workerThreads));
1744 }
1745 }
1746
1747 public void setConfigParams(Map<String, String> configParams) {
1748 String ofPort = configParams.get("openflowport");
1749 if (ofPort != null) {
1750 this.openFlowPort = Integer.parseInt(ofPort);
1751 }
1752 log.debug("OpenFlow port set to {}", this.openFlowPort);
1753 String threads = configParams.get("workerthreads");
1754 if (threads != null) {
1755 this.workerThreads = Integer.parseInt(threads);
1756 }
1757 log.debug("Number of worker threads set to {}", this.workerThreads);
1758 String controllerId = configParams.get("controllerid");
1759 if (controllerId != null) {
1760 this.controllerId = controllerId;
1761 }
Jonathan Hartd10008d2013-02-23 17:04:08 -08001762 else {
1763 //Try to get the hostname of the machine and use that for controller ID
1764 try {
1765 String hostname = java.net.InetAddress.getLocalHost().getHostName();
1766 this.controllerId = hostname;
1767 } catch (UnknownHostException e) {
1768 // Can't get hostname, we'll just use the default
1769 }
1770 }
1771
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001772 log.debug("ControllerId set to {}", this.controllerId);
1773 }
1774
1775 private void initVendorMessages() {
1776 // Configure openflowj to be able to parse the role request/reply
1777 // vendor messages.
1778 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1779 OFNiciraVendorData.NX_VENDOR_ID, 4);
1780 OFVendorId.registerVendorId(niciraVendorId);
1781 OFBasicVendorDataType roleRequestVendorData =
1782 new OFBasicVendorDataType(
1783 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1784 OFRoleRequestVendorData.getInstantiable());
1785 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1786 OFBasicVendorDataType roleReplyVendorData =
1787 new OFBasicVendorDataType(
1788 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1789 OFRoleReplyVendorData.getInstantiable());
1790 niciraVendorId.registerVendorDataType(roleReplyVendorData);
1791 }
1792
1793 /**
1794 * Initialize internal data structures
1795 */
1796 public void init(Map<String, String> configParams) {
1797 // These data structures are initialized here because other
1798 // module's startUp() might be called before ours
1799 this.messageListeners =
1800 new ConcurrentHashMap<OFType,
1801 ListenerDispatcher<OFType,
1802 IOFMessageListener>>();
1803 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001804 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1805 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1806 this.controllerNodeIPsCache = new HashMap<String, String>();
1807 this.updates = new LinkedBlockingQueue<IUpdate>();
1808 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001809 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001810 //Set the controller's role to MASTER so it always tries to do role requests.
1811 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001812 this.roleChanger = new RoleChanger();
1813 initVendorMessages();
1814 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001815 }
1816
1817 /**
1818 * Startup all of the controller's components
1819 */
1820 @LogMessageDoc(message="Waiting for storage source",
1821 explanation="The system database is not yet ready",
1822 recommendation="If this message persists, this indicates " +
1823 "that the system database has failed to start. " +
1824 LogMessageDoc.CHECK_CONTROLLER)
1825 public void startupComponents() {
Jonathan Hartd10008d2013-02-23 17:04:08 -08001826 try {
1827 registryService.registerController(controllerId);
Jonathan Hartb0904bf2013-11-26 14:41:11 -08001828 } catch (RegistryException e) {
1829 log.warn("Registry service error: {}", e.getMessage());
Jonathan Hartd10008d2013-02-23 17:04:08 -08001830 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001831
1832 // Add our REST API
1833 restApi.addRestletRoutable(new CoreWebRoutable());
1834 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001835
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001836 @Override
1837 public Map<String, String> getControllerNodeIPs() {
1838 // We return a copy of the mapping so we can guarantee that
1839 // the mapping return is the same as one that will be (or was)
1840 // dispatched to IHAListeners
1841 HashMap<String,String> retval = new HashMap<String,String>();
1842 synchronized(controllerNodeIPsCache) {
1843 retval.putAll(controllerNodeIPsCache);
1844 }
1845 return retval;
1846 }
1847
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001848 @Override
1849 public long getSystemStartTime() {
1850 return (this.systemStartTime);
1851 }
1852
1853 @Override
1854 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1855 this.alwaysClearFlowsOnSwAdd = value;
1856 }
1857
1858 public boolean getAlwaysClearFlowsOnSwAdd() {
1859 return this.alwaysClearFlowsOnSwAdd;
1860 }
1861}