blob: 38cd5d07774845eb000fd1e24350bd2655b989d3 [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;
60import net.floodlightcontroller.counter.ICounterStoreService;
61import net.floodlightcontroller.packet.Ethernet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063import net.floodlightcontroller.threadpool.IThreadPoolService;
HIGUCHI Yuta36cf0762013-06-14 14:25:38 -070064import net.onrc.onos.ofcontroller.core.IOFSwitchPortListener;
Jonathan Hart8a5d0972013-12-04 10:02:44 -080065import net.onrc.onos.ofcontroller.linkdiscovery.ILinkDiscoveryService;
Jonathan Hartd82f20d2013-02-21 18:04:24 -080066import net.onrc.onos.registry.controller.IControllerRegistryService;
Jonathan Hartcc957a02013-02-26 10:39:04 -080067import net.onrc.onos.registry.controller.IControllerRegistryService.ControlChangeCallback;
Jonathan Hartd10008d2013-02-23 17:04:08 -080068import net.onrc.onos.registry.controller.RegistryException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080069
70import org.jboss.netty.bootstrap.ServerBootstrap;
71import org.jboss.netty.buffer.ChannelBuffer;
72import org.jboss.netty.buffer.ChannelBuffers;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080073import org.jboss.netty.channel.ChannelHandlerContext;
74import org.jboss.netty.channel.ChannelPipelineFactory;
75import org.jboss.netty.channel.ChannelStateEvent;
76import org.jboss.netty.channel.ChannelUpstreamHandler;
77import org.jboss.netty.channel.Channels;
78import org.jboss.netty.channel.ExceptionEvent;
79import org.jboss.netty.channel.MessageEvent;
80import org.jboss.netty.channel.group.ChannelGroup;
81import org.jboss.netty.channel.group.DefaultChannelGroup;
82import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
83import org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler;
84import org.jboss.netty.handler.timeout.IdleStateEvent;
85import org.jboss.netty.handler.timeout.ReadTimeoutException;
86import org.openflow.protocol.OFEchoReply;
87import org.openflow.protocol.OFError;
88import org.openflow.protocol.OFError.OFBadActionCode;
89import org.openflow.protocol.OFError.OFBadRequestCode;
90import org.openflow.protocol.OFError.OFErrorType;
91import org.openflow.protocol.OFError.OFFlowModFailedCode;
92import org.openflow.protocol.OFError.OFHelloFailedCode;
93import org.openflow.protocol.OFError.OFPortModFailedCode;
94import org.openflow.protocol.OFError.OFQueueOpFailedCode;
95import org.openflow.protocol.OFFeaturesReply;
96import org.openflow.protocol.OFGetConfigReply;
97import org.openflow.protocol.OFMessage;
98import org.openflow.protocol.OFPacketIn;
99import org.openflow.protocol.OFPhysicalPort;
Pankaj Berde6a4075d2013-01-22 16:42:54 -0800100import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
Pankaj Berde6debb042013-01-16 18:04:32 -0800101import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800102import org.openflow.protocol.OFPortStatus;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800103import org.openflow.protocol.OFPortStatus.OFPortReason;
104import org.openflow.protocol.OFSetConfig;
105import org.openflow.protocol.OFStatisticsRequest;
106import org.openflow.protocol.OFSwitchConfig;
107import org.openflow.protocol.OFType;
108import org.openflow.protocol.OFVendor;
109import org.openflow.protocol.factory.BasicFactory;
110import org.openflow.protocol.factory.MessageParseException;
111import org.openflow.protocol.statistics.OFDescriptionStatistics;
112import org.openflow.protocol.statistics.OFStatistics;
113import org.openflow.protocol.statistics.OFStatisticsType;
114import org.openflow.protocol.vendor.OFBasicVendorDataType;
115import org.openflow.protocol.vendor.OFBasicVendorId;
116import org.openflow.protocol.vendor.OFVendorId;
117import org.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800118import org.openflow.vendor.nicira.OFNiciraVendorData;
119import org.openflow.vendor.nicira.OFRoleReplyVendorData;
120import org.openflow.vendor.nicira.OFRoleRequestVendorData;
121import org.openflow.vendor.nicira.OFRoleVendorData;
122import org.slf4j.Logger;
123import org.slf4j.LoggerFactory;
124
125
126/**
127 * The main controller class. Handles all setup and network listeners
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700128 *
129 * Extensions made by ONOS are:
130 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
131 * Available as net.onrc.onos.ofcontroller.core.IOFSwitchPortListener
132 * - Distributed ownership control of switch through RegistryService(IControllerRegistryService)
Pavlin Radoslavova653e9f2013-10-16 03:08:52 -0700133 * - Register ONOS services. (IControllerRegistryService)
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700134 * - Additional DEBUG logs
135 * - Try using hostname as controller ID, when ID was not explicitly given.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800136 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800137public class Controller implements IFloodlightProviderService {
HIGUCHI Yuta0ba6fd02013-06-14 12:46:56 -0700138
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700139 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800140
141 private static final String ERROR_DATABASE =
142 "The controller could not communicate with the system database.";
143
144 protected BasicFactory factory;
145 protected ConcurrentMap<OFType,
146 ListenerDispatcher<OFType,IOFMessageListener>>
147 messageListeners;
148 // The activeSwitches map contains only those switches that are actively
149 // being controlled by us -- it doesn't contain switches that are
150 // in the slave role
151 protected ConcurrentHashMap<Long, IOFSwitch> activeSwitches;
152 // connectedSwitches contains all connected switches, including ones where
153 // we're a slave controller. We need to keep track of them so that we can
154 // send role request messages to switches when our role changes to master
155 // We add a switch to this set after it successfully completes the
156 // handshake. Access to this Set needs to be synchronized with roleChanger
157 protected HashSet<OFSwitchImpl> connectedSwitches;
158
159 // The controllerNodeIPsCache maps Controller IDs to their IP address.
160 // It's only used by handleControllerNodeIPsChanged
161 protected HashMap<String, String> controllerNodeIPsCache;
162
163 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800164 protected BlockingQueue<IUpdate> updates;
165
166 // Module dependencies
167 protected IRestApiService restApi;
168 protected ICounterStoreService counterStore = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800169 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800170 protected IControllerRegistryService registryService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800171
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800172 protected ILinkDiscoveryService linkDiscovery;
173
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800174 // Configuration options
175 protected int openFlowPort = 6633;
176 protected int workerThreads = 0;
177 // The id for this controller node. Should be unique for each controller
178 // node in a controller cluster.
179 protected String controllerId = "localhost";
180
181 // The current role of the controller.
182 // If the controller isn't configured to support roles, then this is null.
183 protected Role role;
184 // A helper that handles sending and timeout handling for role requests
185 protected RoleChanger roleChanger;
186
187 // Start time of the controller
188 protected long systemStartTime;
189
190 // Flag to always flush flow table on switch reconnect (HA or otherwise)
191 protected boolean alwaysClearFlowsOnSwAdd = false;
192
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800193 // Perf. related configuration
194 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
195 protected static final int BATCH_MAX_SIZE = 100;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700196 protected static final boolean ALWAYS_DECODE_ETH = true;
197
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800198 public enum SwitchUpdateType {
199 ADDED,
200 REMOVED,
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700201 PORTCHANGED,
202 PORTADDED,
203 PORTREMOVED
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 }
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700205
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800206 /**
207 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700208 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800209 */
210 protected class SwitchUpdate implements IUpdate {
211 public IOFSwitch sw;
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700212 public OFPhysicalPort port; // Added by ONOS
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800213 public SwitchUpdateType switchUpdateType;
214 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
215 this.sw = sw;
216 this.switchUpdateType = switchUpdateType;
217 }
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 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800223 public void dispatch() {
224 if (log.isTraceEnabled()) {
225 log.trace("Dispatching switch update {} {}",
226 sw, switchUpdateType);
227 }
228 if (switchListeners != null) {
229 for (IOFSwitchListener listener : switchListeners) {
230 switch(switchUpdateType) {
231 case ADDED:
232 listener.addedSwitch(sw);
233 break;
234 case REMOVED:
235 listener.removedSwitch(sw);
236 break;
237 case PORTCHANGED:
238 listener.switchPortChanged(sw.getId());
239 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700240 case PORTADDED:
HIGUCHI Yuta36cf0762013-06-14 14:25:38 -0700241 if (listener instanceof IOFSwitchPortListener) {
242 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
243 }
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700244 break;
245 case PORTREMOVED:
HIGUCHI Yuta36cf0762013-06-14 14:25:38 -0700246 if (listener instanceof IOFSwitchPortListener) {
247 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
248 }
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700249 break;
250 default:
251 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800252 }
253 }
254 }
255 }
256 }
257
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800258 // ***************
259 // Getters/Setters
260 // ***************
261
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800262 public void setCounterStore(ICounterStoreService counterStore) {
263 this.counterStore = counterStore;
264 }
265
mininet73e7fb72013-12-03 14:25:53 -0800266
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800267
268 public void setRestApiService(IRestApiService restApi) {
269 this.restApi = restApi;
270 }
271
272 public void setThreadPoolService(IThreadPoolService tp) {
273 this.threadPool = tp;
274 }
275
Jonathan Hartd82f20d2013-02-21 18:04:24 -0800276 public void setMastershipService(IControllerRegistryService serviceImpl) {
Jonathan Hartd10008d2013-02-23 17:04:08 -0800277 this.registryService = serviceImpl;
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800278 }
279
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800280 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
281 this.linkDiscovery = linkDiscovery;
282 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800283
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700284 public void publishUpdate(IUpdate update) {
285 try {
286 this.updates.put(update);
287 } catch (InterruptedException e) {
288 log.error("Failure adding update to queue", e);
289 }
290 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291
292 // **********************
293 // ChannelUpstreamHandler
294 // **********************
295
296 /**
297 * Return a new channel handler for processing a switch connections
298 * @param state The channel state object for the connection
299 * @return the new channel handler
300 */
301 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
302 return new OFChannelHandler(state);
303 }
304
Jonathan Hartcc957a02013-02-26 10:39:04 -0800305 protected class RoleChangeCallback implements ControlChangeCallback {
306 @Override
307 public void controlChanged(long dpid, boolean hasControl) {
308 log.info("Role change callback for switch {}, hasControl {}",
309 HexString.toHexString(dpid), hasControl);
310
311 synchronized(roleChanger){
312 OFSwitchImpl sw = null;
313 for (OFSwitchImpl connectedSw : connectedSwitches){
314 if (connectedSw.getId() == dpid){
315 sw = connectedSw;
316 break;
317 }
318 }
319 if (sw == null){
320 log.warn("Switch {} not found in connected switches",
321 HexString.toHexString(dpid));
322 return;
323 }
324
325 Role role = null;
326
Pankaj Berde01939e92013-03-08 14:38:27 -0800327 /*
328 * issue #229
329 * Cannot rely on sw.getRole() as it can be behind due to pending
330 * role changes in the queue. Just submit it and late the RoleChanger
331 * handle duplicates.
332 */
333
334 if (hasControl){
Jonathan Hartcc957a02013-02-26 10:39:04 -0800335 role = Role.MASTER;
336 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800337 else {
Jonathan Hartcc957a02013-02-26 10:39:04 -0800338 role = Role.SLAVE;
339 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800340
341 log.debug("Sending role request {} msg to {}", role, sw);
342 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
343 swList.add(sw);
344 roleChanger.submitRequest(swList, role);
345
Jonathan Hartcc957a02013-02-26 10:39:04 -0800346 }
347
348 }
349 }
350
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800351 /**
352 * Channel handler deals with the switch connection and dispatches
353 * switch messages to the appropriate locations.
354 * @author readams
355 */
356 protected class OFChannelHandler
357 extends IdleStateAwareChannelUpstreamHandler {
358 protected OFSwitchImpl sw;
359 protected OFChannelState state;
360
361 public OFChannelHandler(OFChannelState state) {
362 this.state = state;
363 }
364
365 @Override
366 @LogMessageDoc(message="New switch connection from {ip address}",
367 explanation="A new switch has connected from the " +
368 "specified IP address")
369 public void channelConnected(ChannelHandlerContext ctx,
370 ChannelStateEvent e) throws Exception {
371 log.info("New switch connection from {}",
372 e.getChannel().getRemoteAddress());
373
374 sw = new OFSwitchImpl();
375 sw.setChannel(e.getChannel());
376 sw.setFloodlightProvider(Controller.this);
377 sw.setThreadPoolService(threadPool);
378
379 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
380 msglist.add(factory.getMessage(OFType.HELLO));
381 e.getChannel().write(msglist);
382
383 }
384
385 @Override
386 @LogMessageDoc(message="Disconnected switch {switch information}",
387 explanation="The specified switch has disconnected.")
388 public void channelDisconnected(ChannelHandlerContext ctx,
389 ChannelStateEvent e) throws Exception {
390 if (sw != null && state.hsState == HandshakeState.READY) {
391 if (activeSwitches.containsKey(sw.getId())) {
392 // It's safe to call removeSwitch even though the map might
393 // not contain this particular switch but another with the
394 // same DPID
395 removeSwitch(sw);
396 }
397 synchronized(roleChanger) {
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700398 if (controlRequested) {
399 registryService.releaseControl(sw.getId());
400 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800401 connectedSwitches.remove(sw);
402 }
403 sw.setConnected(false);
404 }
405 log.info("Disconnected switch {}", sw);
406 }
407
408 @Override
409 @LogMessageDocs({
410 @LogMessageDoc(level="ERROR",
411 message="Disconnecting switch {switch} due to read timeout",
412 explanation="The connected switch has failed to send any " +
413 "messages or respond to echo requests",
414 recommendation=LogMessageDoc.CHECK_SWITCH),
415 @LogMessageDoc(level="ERROR",
416 message="Disconnecting switch {switch}: failed to " +
417 "complete handshake",
418 explanation="The switch did not respond correctly " +
419 "to handshake messages",
420 recommendation=LogMessageDoc.CHECK_SWITCH),
421 @LogMessageDoc(level="ERROR",
422 message="Disconnecting switch {switch} due to IO Error: {}",
423 explanation="There was an error communicating with the switch",
424 recommendation=LogMessageDoc.CHECK_SWITCH),
425 @LogMessageDoc(level="ERROR",
426 message="Disconnecting switch {switch} due to switch " +
427 "state error: {error}",
428 explanation="The switch sent an unexpected message",
429 recommendation=LogMessageDoc.CHECK_SWITCH),
430 @LogMessageDoc(level="ERROR",
431 message="Disconnecting switch {switch} due to " +
432 "message parse failure",
433 explanation="Could not parse a message from the switch",
434 recommendation=LogMessageDoc.CHECK_SWITCH),
435 @LogMessageDoc(level="ERROR",
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800436 message="Could not process message: queue full",
437 explanation="OpenFlow messages are arriving faster than " +
438 " the controller can process them.",
439 recommendation=LogMessageDoc.CHECK_CONTROLLER),
440 @LogMessageDoc(level="ERROR",
441 message="Error while processing message " +
442 "from switch {switch} {cause}",
443 explanation="An error occurred processing the switch message",
444 recommendation=LogMessageDoc.GENERIC_ACTION)
445 })
446 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
447 throws Exception {
448 if (e.getCause() instanceof ReadTimeoutException) {
449 // switch timeout
450 log.error("Disconnecting switch {} due to read timeout", sw);
451 ctx.getChannel().close();
452 } else if (e.getCause() instanceof HandshakeTimeoutException) {
453 log.error("Disconnecting switch {}: failed to complete handshake",
454 sw);
455 ctx.getChannel().close();
456 } else if (e.getCause() instanceof ClosedChannelException) {
457 //log.warn("Channel for sw {} already closed", sw);
458 } else if (e.getCause() instanceof IOException) {
459 log.error("Disconnecting switch {} due to IO Error: {}",
460 sw, e.getCause().getMessage());
461 ctx.getChannel().close();
462 } else if (e.getCause() instanceof SwitchStateException) {
463 log.error("Disconnecting switch {} due to switch state error: {}",
464 sw, e.getCause().getMessage());
465 ctx.getChannel().close();
466 } else if (e.getCause() instanceof MessageParseException) {
467 log.error("Disconnecting switch " + sw +
468 " due to message parse failure",
469 e.getCause());
470 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800471 } else if (e.getCause() instanceof RejectedExecutionException) {
472 log.warn("Could not process message: queue full");
473 } else {
474 log.error("Error while processing message from switch " + sw,
475 e.getCause());
476 ctx.getChannel().close();
477 }
478 }
479
480 @Override
481 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
482 throws Exception {
483 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
484 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
485 e.getChannel().write(msglist);
486 }
487
488 @Override
489 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
490 throws Exception {
491 if (e.getMessage() instanceof List) {
492 @SuppressWarnings("unchecked")
493 List<OFMessage> msglist = (List<OFMessage>)e.getMessage();
494
495 for (OFMessage ofm : msglist) {
496 try {
497 processOFMessage(ofm);
498 }
499 catch (Exception ex) {
500 // We are the last handler in the stream, so run the
501 // exception through the channel again by passing in
502 // ctx.getChannel().
503 Channels.fireExceptionCaught(ctx.getChannel(), ex);
504 }
505 }
506
507 // Flush all flow-mods/packet-out generated from this "train"
508 OFSwitchImpl.flush_all();
509 }
510 }
511
512 /**
513 * Process the request for the switch description
514 */
515 @LogMessageDoc(level="ERROR",
516 message="Exception in reading description " +
517 " during handshake {exception}",
518 explanation="Could not process the switch description string",
519 recommendation=LogMessageDoc.CHECK_SWITCH)
520 void processSwitchDescReply() {
521 try {
522 // Read description, if it has been updated
523 @SuppressWarnings("unchecked")
524 Future<List<OFStatistics>> desc_future =
525 (Future<List<OFStatistics>>)sw.
526 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
527 List<OFStatistics> values =
528 desc_future.get(0, TimeUnit.MILLISECONDS);
529 if (values != null) {
530 OFDescriptionStatistics description =
531 new OFDescriptionStatistics();
532 ChannelBuffer data =
533 ChannelBuffers.buffer(description.getLength());
534 for (OFStatistics f : values) {
535 f.writeTo(data);
536 description.readFrom(data);
537 break; // SHOULD be a list of length 1
538 }
539 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
540 description);
541 sw.setSwitchProperties(description);
542 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800543 }
Jonathan Hart2fa28062013-11-25 20:16:28 -0800544
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800545 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
546 state.hasDescription = true;
547 checkSwitchReady();
548 }
549 catch (InterruptedException ex) {
550 // Ignore
551 }
552 catch (TimeoutException ex) {
553 // Ignore
554 } catch (Exception ex) {
555 log.error("Exception in reading description " +
556 " during handshake", ex);
557 }
558 }
559
560 /**
561 * Send initial switch setup information that we need before adding
562 * the switch
563 * @throws IOException
564 */
565 void sendHelloConfiguration() throws IOException {
566 // Send initial Features Request
Jonathan Hart9e92c512013-03-20 16:24:44 -0700567 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800568 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
569 }
570
571 /**
572 * Send the configuration requests we can only do after we have
573 * the features reply
574 * @throws IOException
575 */
576 void sendFeatureReplyConfiguration() throws IOException {
Jonathan Hart9e92c512013-03-20 16:24:44 -0700577 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800578 // Ensure we receive the full packet via PacketIn
579 OFSetConfig config = (OFSetConfig) factory
580 .getMessage(OFType.SET_CONFIG);
581 config.setMissSendLength((short) 0xffff)
582 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
583 sw.write(config, null);
584 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
585 null);
586
587 // Get Description to set switch-specific flags
588 OFStatisticsRequest req = new OFStatisticsRequest();
589 req.setStatisticType(OFStatisticsType.DESC);
590 req.setLengthU(req.getLengthU());
591 Future<List<OFStatistics>> dfuture =
592 sw.getStatistics(req);
593 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
594 dfuture);
595
596 }
HIGUCHI Yuta0ba6fd02013-06-14 12:46:56 -0700597
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700598 volatile Boolean controlRequested = Boolean.FALSE;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800599 protected void checkSwitchReady() {
600 if (state.hsState == HandshakeState.FEATURES_REPLY &&
601 state.hasDescription && state.hasGetConfigReply) {
602
603 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700604 log.debug("Handshake with {} complete", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800605
606 synchronized(roleChanger) {
607 // We need to keep track of all of the switches that are connected
608 // to the controller, in any role, so that we can later send the
609 // role request messages when the controller role changes.
610 // We need to be synchronized while doing this: we must not
611 // send a another role request to the connectedSwitches until
612 // we were able to add this new switch to connectedSwitches
613 // *and* send the current role to the new switch.
614 connectedSwitches.add(sw);
615
616 if (role != null) {
Jonathan Hart97801ac2013-02-26 14:29:16 -0800617 //Put the switch in SLAVE mode until we know we have control
618 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
619 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
620 swList.add(sw);
621 roleChanger.submitRequest(swList, Role.SLAVE);
622
Jonathan Hartcc957a02013-02-26 10:39:04 -0800623 //Request control of the switch from the global registry
624 try {
Pankaj Berdeda7187b2013-03-18 15:24:59 -0700625 controlRequested = Boolean.TRUE;
Jonathan Hartcc957a02013-02-26 10:39:04 -0800626 registryService.requestControl(sw.getId(),
627 new RoleChangeCallback());
628 } catch (RegistryException e) {
629 log.debug("Registry error: {}", e.getMessage());
Pankaj Berde99fcee12013-03-18 09:41:53 -0700630 controlRequested = Boolean.FALSE;
Jonathan Hartcc957a02013-02-26 10:39:04 -0800631 }
632
Jonathan Hart97801ac2013-02-26 14:29:16 -0800633
Jonathan Hartcc957a02013-02-26 10:39:04 -0800634
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800635 // Send a role request if role support is enabled for the controller
636 // This is a probe that we'll use to determine if the switch
637 // actually supports the role request message. If it does we'll
638 // get back a role reply message. If it doesn't we'll get back an
639 // OFError message.
640 // If role is MASTER we will promote switch to active
641 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800642 /*
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800643 log.debug("This controller's role is {}, " +
644 "sending initial role request msg to {}",
645 role, sw);
646 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
647 swList.add(sw);
648 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800649 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800650 }
651 else {
652 // Role supported not enabled on controller (for now)
653 // automatically promote switch to active state.
Umesh Krishnaswamyb56bb292013-02-12 20:28:27 -0800654 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800655 "not sending role request msg to {}",
656 role, sw);
657 // Need to clear FlowMods before we add the switch
658 // and dispatch updates otherwise we have a race condition.
659 sw.clearAllFlowMods();
660 addSwitch(sw);
661 state.firstRoleReplyReceived = true;
662 }
663 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700664 if (!controlRequested) {
665 // yield to allow other thread(s) to release control
666 try {
667 Thread.sleep(10);
668 } catch (InterruptedException e) {
669 // Ignore interruptions
670 }
671 // safer to bounce the switch to reconnect here than proceeding further
Jonathan Hart9e92c512013-03-20 16:24:44 -0700672 log.debug("Closing {} because we weren't able to request control " +
673 "successfully" + sw);
Pankaj Berde99fcee12013-03-18 09:41:53 -0700674 sw.channel.close();
675 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800676 }
677 }
678
679 /* Handle a role reply message we received from the switch. Since
680 * netty serializes message dispatch we don't need to synchronize
681 * against other receive operations from the same switch, so no need
682 * to synchronize addSwitch(), removeSwitch() operations from the same
683 * connection.
684 * FIXME: However, when a switch with the same DPID connects we do
685 * need some synchronization. However, handling switches with same
686 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
687 * removedSwitch notification):1
688 *
689 */
690 @LogMessageDoc(level="ERROR",
691 message="Invalid role value in role reply message",
692 explanation="Was unable to set the HA role (master or slave) " +
693 "for the controller.",
694 recommendation=LogMessageDoc.CHECK_CONTROLLER)
695 protected void handleRoleReplyMessage(OFVendor vendorMessage,
696 OFRoleReplyVendorData roleReplyVendorData) {
697 // Map from the role code in the message to our role enum
698 int nxRole = roleReplyVendorData.getRole();
699 Role role = null;
700 switch (nxRole) {
701 case OFRoleVendorData.NX_ROLE_OTHER:
702 role = Role.EQUAL;
703 break;
704 case OFRoleVendorData.NX_ROLE_MASTER:
705 role = Role.MASTER;
706 break;
707 case OFRoleVendorData.NX_ROLE_SLAVE:
708 role = Role.SLAVE;
709 break;
710 default:
711 log.error("Invalid role value in role reply message");
712 sw.getChannel().close();
713 return;
714 }
715
716 log.debug("Handling role reply for role {} from {}. " +
717 "Controller's role is {} ",
718 new Object[] { role, sw, Controller.this.role}
719 );
720
721 sw.deliverRoleReply(vendorMessage.getXid(), role);
722
723 boolean isActive = activeSwitches.containsKey(sw.getId());
724 if (!isActive && sw.isActive()) {
725 // Transition from SLAVE to MASTER.
726
727 if (!state.firstRoleReplyReceived ||
728 getAlwaysClearFlowsOnSwAdd()) {
729 // This is the first role-reply message we receive from
730 // this switch or roles were disabled when the switch
731 // connected:
732 // Delete all pre-existing flows for new connections to
733 // the master
734 //
735 // FIXME: Need to think more about what the test should
736 // be for when we flush the flow-table? For example,
737 // if all the controllers are temporarily in the backup
738 // role (e.g. right after a failure of the master
739 // controller) at the point the switch connects, then
740 // all of the controllers will initially connect as
741 // backup controllers and not flush the flow-table.
742 // Then when one of them is promoted to master following
743 // the master controller election the flow-table
744 // will still not be flushed because that's treated as
745 // a failover event where we don't want to flush the
746 // flow-table. The end result would be that the flow
747 // table for a newly connected switch is never
748 // flushed. Not sure how to handle that case though...
749 sw.clearAllFlowMods();
750 log.debug("First role reply from master switch {}, " +
751 "clear FlowTable to active switch list",
752 HexString.toHexString(sw.getId()));
753 }
754
755 // Some switches don't seem to update us with port
756 // status messages while in slave role.
Jonathan Hart2fa28062013-11-25 20:16:28 -0800757 //readSwitchPortStateFromStorage(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800758
759 // Only add the switch to the active switch list if
760 // we're not in the slave role. Note that if the role
761 // attribute is null, then that means that the switch
762 // doesn't support the role request messages, so in that
763 // case we're effectively in the EQUAL role and the
764 // switch should be included in the active switch list.
765 addSwitch(sw);
766 log.debug("Added master switch {} to active switch list",
767 HexString.toHexString(sw.getId()));
768
769 }
770 else if (isActive && !sw.isActive()) {
771 // Transition from MASTER to SLAVE: remove switch
772 // from active switch list.
773 log.debug("Removed slave switch {} from active switch" +
774 " list", HexString.toHexString(sw.getId()));
775 removeSwitch(sw);
776 }
777
778 // Indicate that we have received a role reply message.
779 state.firstRoleReplyReceived = true;
780 }
781
782 protected boolean handleVendorMessage(OFVendor vendorMessage) {
783 boolean shouldHandleMessage = false;
784 int vendor = vendorMessage.getVendor();
785 switch (vendor) {
786 case OFNiciraVendorData.NX_VENDOR_ID:
787 OFNiciraVendorData niciraVendorData =
788 (OFNiciraVendorData)vendorMessage.getVendorData();
789 int dataType = niciraVendorData.getDataType();
790 switch (dataType) {
791 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
792 OFRoleReplyVendorData roleReplyVendorData =
793 (OFRoleReplyVendorData) niciraVendorData;
794 handleRoleReplyMessage(vendorMessage,
795 roleReplyVendorData);
796 break;
797 default:
798 log.warn("Unhandled Nicira VENDOR message; " +
799 "data type = {}", dataType);
800 break;
801 }
802 break;
803 default:
804 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
805 break;
806 }
807
808 return shouldHandleMessage;
809 }
810
811 /**
812 * Dispatch an Openflow message from a switch to the appropriate
813 * handler.
814 * @param m The message to process
815 * @throws IOException
816 * @throws SwitchStateException
817 */
818 @LogMessageDocs({
819 @LogMessageDoc(level="WARN",
820 message="Config Reply from {switch} has " +
821 "miss length set to {length}",
822 explanation="The controller requires that the switch " +
823 "use a miss length of 0xffff for correct " +
824 "function",
825 recommendation="Use a different switch to ensure " +
826 "correct function"),
827 @LogMessageDoc(level="WARN",
828 message="Received ERROR from sw {switch} that "
829 +"indicates roles are not supported "
830 +"but we have received a valid "
831 +"role reply earlier",
832 explanation="The switch sent a confusing message to the" +
833 "controller")
834 })
835 protected void processOFMessage(OFMessage m)
836 throws IOException, SwitchStateException {
837 boolean shouldHandleMessage = false;
838
839 switch (m.getType()) {
840 case HELLO:
841 if (log.isTraceEnabled())
842 log.trace("HELLO from {}", sw);
843
844 if (state.hsState.equals(HandshakeState.START)) {
845 state.hsState = HandshakeState.HELLO;
846 sendHelloConfiguration();
847 } else {
848 throw new SwitchStateException("Unexpected HELLO from "
849 + sw);
850 }
851 break;
852 case ECHO_REQUEST:
853 OFEchoReply reply =
854 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
855 reply.setXid(m.getXid());
856 sw.write(reply, null);
857 break;
858 case ECHO_REPLY:
859 break;
860 case FEATURES_REPLY:
861 if (log.isTraceEnabled())
862 log.trace("Features Reply from {}", sw);
863
864 sw.setFeaturesReply((OFFeaturesReply) m);
865 if (state.hsState.equals(HandshakeState.HELLO)) {
866 sendFeatureReplyConfiguration();
867 state.hsState = HandshakeState.FEATURES_REPLY;
868 // uncomment to enable "dumb" switches like cbench
869 // state.hsState = HandshakeState.READY;
870 // addSwitch(sw);
871 } else {
872 // return results to rest api caller
873 sw.deliverOFFeaturesReply(m);
874 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800875 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800876 }
877 break;
878 case GET_CONFIG_REPLY:
879 if (log.isTraceEnabled())
880 log.trace("Get config reply from {}", sw);
881
882 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
883 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
884 throw new SwitchStateException(em);
885 }
886 OFGetConfigReply cr = (OFGetConfigReply) m;
887 if (cr.getMissSendLength() == (short)0xffff) {
888 log.trace("Config Reply from {} confirms " +
889 "miss length set to 0xffff", sw);
890 } else {
891 log.warn("Config Reply from {} has " +
892 "miss length set to {}",
893 sw, cr.getMissSendLength() & 0xffff);
894 }
895 state.hasGetConfigReply = true;
896 checkSwitchReady();
897 break;
898 case VENDOR:
899 shouldHandleMessage = handleVendorMessage((OFVendor)m);
900 break;
901 case ERROR:
Jonathan Hart3525df92013-03-19 14:09:13 -0700902 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800903 // TODO: we need better error handling. Especially for
904 // request/reply style message (stats, roles) we should have
905 // a unified way to lookup the xid in the error message.
906 // This will probable involve rewriting the way we handle
907 // request/reply style messages.
908 OFError error = (OFError) m;
909 boolean shouldLogError = true;
910 // TODO: should we check that firstRoleReplyReceived is false,
911 // i.e., check only whether the first request fails?
912 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
913 boolean isBadVendorError =
914 (error.getErrorType() == OFError.OFErrorType.
915 OFPET_BAD_REQUEST.getValue());
916 // We expect to receive a bad vendor error when
917 // we're connected to a switch that doesn't support
918 // the Nicira vendor extensions (i.e. not OVS or
919 // derived from OVS). By protocol, it should also be
920 // BAD_VENDOR, but too many switch implementations
921 // get it wrong and we can already check the xid()
922 // so we can ignore the type with confidence that this
923 // is not a spurious error
924 shouldLogError = !isBadVendorError;
925 if (isBadVendorError) {
Jonathan Hart3525df92013-03-19 14:09:13 -0700926 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800927 if (state.firstRoleReplyReceived && (role != null)) {
928 log.warn("Received ERROR from sw {} that "
929 +"indicates roles are not supported "
930 +"but we have received a valid "
931 +"role reply earlier", sw);
932 }
933 state.firstRoleReplyReceived = true;
Jonathan Harta95c6d92013-03-18 16:12:27 -0700934 Role requestedRole =
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700935 sw.deliverRoleRequestNotSupportedEx(error.getXid());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800936 synchronized(roleChanger) {
937 if (sw.role == null && Controller.this.role==Role.SLAVE) {
Jonathan Harta95c6d92013-03-18 16:12:27 -0700938 //This will now never happen. The Controller's role
939 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800940 // the switch doesn't understand role request
941 // messages and the current controller role is
942 // slave. We need to disconnect the switch.
943 // @see RoleChanger for rationale
Jonathan Hart9e92c512013-03-20 16:24:44 -0700944 log.warn("Closing {} channel because controller's role " +
945 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800946 sw.getChannel().close();
947 }
Jonathan Harta95c6d92013-03-18 16:12:27 -0700948 else if (sw.role == null && requestedRole == Role.MASTER) {
Jonathan Hart3525df92013-03-19 14:09:13 -0700949 log.debug("Adding switch {} because we got an error" +
950 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800951 // Controller's role is master: add to
952 // active
953 // TODO: check if clearing flow table is
954 // right choice here.
955 // Need to clear FlowMods before we add the switch
956 // and dispatch updates otherwise we have a race condition.
957 // TODO: switch update is async. Won't we still have a potential
958 // race condition?
959 sw.clearAllFlowMods();
960 addSwitch(sw);
961 }
962 }
963 }
964 else {
965 // TODO: Is this the right thing to do if we receive
966 // some other error besides a bad vendor error?
967 // Presumably that means the switch did actually
968 // understand the role request message, but there
969 // was some other error from processing the message.
970 // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED
971 // error code, but it doesn't look like the Nicira
972 // role request has that. Should check OVS source
973 // code to see if it's possible for any other errors
974 // to be returned.
975 // If we received an error the switch is not
976 // in the correct role, so we need to disconnect it.
977 // We could also resend the request but then we need to
978 // check if there are other pending request in which
979 // case we shouldn't resend. If we do resend we need
980 // to make sure that the switch eventually accepts one
981 // of our requests or disconnect the switch. This feels
982 // cumbersome.
Jonathan Hart9e92c512013-03-20 16:24:44 -0700983 log.debug("Closing {} channel because we recieved an " +
984 "error other than BAD_VENDOR", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800985 sw.getChannel().close();
986 }
987 }
988 // Once we support OF 1.2, we'd add code to handle it here.
989 //if (error.getXid() == state.ofRoleRequestXid) {
990 //}
991 if (shouldLogError)
992 logError(sw, error);
993 break;
994 case STATS_REPLY:
995 if (state.hsState.ordinal() <
996 HandshakeState.FEATURES_REPLY.ordinal()) {
997 String em = "Unexpected STATS_REPLY from " + sw;
998 throw new SwitchStateException(em);
999 }
1000 sw.deliverStatisticsReply(m);
1001 if (sw.hasAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE)) {
1002 processSwitchDescReply();
1003 }
1004 break;
1005 case PORT_STATUS:
1006 // We want to update our port state info even if we're in
1007 // the slave role, but we only want to update storage if
1008 // we're the master (or equal).
1009 boolean updateStorage = state.hsState.
1010 equals(HandshakeState.READY) &&
1011 (sw.getRole() != Role.SLAVE);
1012 handlePortStatusMessage(sw, (OFPortStatus)m, updateStorage);
1013 shouldHandleMessage = true;
1014 break;
1015
1016 default:
1017 shouldHandleMessage = true;
1018 break;
1019 }
1020
1021 if (shouldHandleMessage) {
1022 sw.getListenerReadLock().lock();
1023 try {
1024 if (sw.isConnected()) {
1025 if (!state.hsState.equals(HandshakeState.READY)) {
1026 log.debug("Ignoring message type {} received " +
1027 "from switch {} before switch is " +
1028 "fully configured.", m.getType(), sw);
1029 }
1030 // Check if the controller is in the slave role for the
1031 // switch. If it is, then don't dispatch the message to
1032 // the listeners.
1033 // TODO: Should we dispatch messages that we expect to
1034 // receive when we're in the slave role, e.g. port
1035 // status messages? Since we're "hiding" switches from
1036 // the listeners when we're in the slave role, then it
1037 // seems a little weird to dispatch port status messages
1038 // to them. On the other hand there might be special
1039 // modules that care about all of the connected switches
1040 // and would like to receive port status notifications.
1041 else if (sw.getRole() == Role.SLAVE) {
1042 // Don't log message if it's a port status message
1043 // since we expect to receive those from the switch
1044 // and don't want to emit spurious messages.
1045 if (m.getType() != OFType.PORT_STATUS) {
1046 log.debug("Ignoring message type {} received " +
1047 "from switch {} while in the slave role.",
1048 m.getType(), sw);
1049 }
1050 } else {
1051 handleMessage(sw, m, null);
1052 }
1053 }
1054 }
1055 finally {
1056 sw.getListenerReadLock().unlock();
1057 }
1058 }
1059 }
1060 }
1061
1062 // ****************
1063 // Message handlers
1064 // ****************
1065
1066 protected void handlePortStatusMessage(IOFSwitch sw,
1067 OFPortStatus m,
1068 boolean updateStorage) {
1069 short portNumber = m.getDesc().getPortNumber();
1070 OFPhysicalPort port = m.getDesc();
1071 if (m.getReason() == (byte)OFPortReason.OFPPR_MODIFY.ordinal()) {
Pankaj Berde6a4075d2013-01-22 16:42:54 -08001072 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1073 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001074 sw.setPort(port);
Pankaj Berde6a4075d2013-01-22 16:42:54 -08001075 if (!portDown) {
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001076 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1077 try {
1078 this.updates.put(update);
1079 } catch (InterruptedException e) {
1080 log.error("Failure adding update to queue", e);
1081 }
Pankaj Berde6debb042013-01-16 18:04:32 -08001082 } else {
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001083 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1084 try {
1085 this.updates.put(update);
1086 } catch (InterruptedException e) {
1087 log.error("Failure adding update to queue", e);
1088 }
Pankaj Berde6debb042013-01-16 18:04:32 -08001089 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001090 //if (updateStorage)
1091 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001092 log.debug("Port #{} modified for {}", portNumber, sw);
1093 } else if (m.getReason() == (byte)OFPortReason.OFPPR_ADD.ordinal()) {
Jonathan Hart8a5d0972013-12-04 10:02:44 -08001094 // XXX Workaround to prevent race condition where a link is detected
1095 // and attempted to be written to the database before the port is in
1096 // the database. We now suppress link discovery on ports until we're
1097 // sure they're in the database.
1098 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1099
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001100 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001101 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1102 try {
1103 this.updates.put(update);
1104 } catch (InterruptedException e) {
1105 log.error("Failure adding update to queue", e);
1106 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001107 //if (updateStorage)
1108 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001109 log.debug("Port #{} added for {}", portNumber, sw);
1110 } else if (m.getReason() ==
1111 (byte)OFPortReason.OFPPR_DELETE.ordinal()) {
1112 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001113 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1114 try {
1115 this.updates.put(update);
1116 } catch (InterruptedException e) {
1117 log.error("Failure adding update to queue", e);
1118 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001119 //if (updateStorage)
1120 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001121 log.debug("Port #{} deleted for {}", portNumber, sw);
1122 }
1123 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1124 try {
1125 this.updates.put(update);
1126 } catch (InterruptedException e) {
1127 log.error("Failure adding update to queue", e);
1128 }
1129 }
1130
1131 /**
1132 * flcontext_cache - Keep a thread local stack of contexts
1133 */
1134 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
1135 new ThreadLocal <Stack<FloodlightContext>> () {
1136 @Override
1137 protected Stack<FloodlightContext> initialValue() {
1138 return new Stack<FloodlightContext>();
1139 }
1140 };
1141
1142 /**
1143 * flcontext_alloc - pop a context off the stack, if required create a new one
1144 * @return FloodlightContext
1145 */
1146 protected static FloodlightContext flcontext_alloc() {
1147 FloodlightContext flcontext = null;
1148
1149 if (flcontext_cache.get().empty()) {
1150 flcontext = new FloodlightContext();
1151 }
1152 else {
1153 flcontext = flcontext_cache.get().pop();
1154 }
1155
1156 return flcontext;
1157 }
1158
1159 /**
1160 * flcontext_free - Free the context to the current thread
1161 * @param flcontext
1162 */
1163 protected void flcontext_free(FloodlightContext flcontext) {
1164 flcontext.getStorage().clear();
1165 flcontext_cache.get().push(flcontext);
1166 }
1167
1168 /**
1169 * Handle replies to certain OFMessages, and pass others off to listeners
1170 * @param sw The switch for the message
1171 * @param m The message
1172 * @param bContext The floodlight context. If null then floodlight context would
1173 * be allocated in this function
1174 * @throws IOException
1175 */
1176 @LogMessageDocs({
1177 @LogMessageDoc(level="ERROR",
1178 message="Ignoring PacketIn (Xid = {xid}) because the data" +
1179 " field is empty.",
1180 explanation="The switch sent an improperly-formatted PacketIn" +
1181 " message",
1182 recommendation=LogMessageDoc.CHECK_SWITCH),
1183 @LogMessageDoc(level="WARN",
1184 message="Unhandled OF Message: {} from {}",
1185 explanation="The switch sent a message not handled by " +
1186 "the controller")
1187 })
1188 protected void handleMessage(IOFSwitch sw, OFMessage m,
1189 FloodlightContext bContext)
1190 throws IOException {
1191 Ethernet eth = null;
1192
1193 switch (m.getType()) {
1194 case PACKET_IN:
1195 OFPacketIn pi = (OFPacketIn)m;
1196
1197 if (pi.getPacketData().length <= 0) {
1198 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1199 ") because the data field is empty.");
1200 return;
1201 }
1202
1203 if (Controller.ALWAYS_DECODE_ETH) {
1204 eth = new Ethernet();
1205 eth.deserialize(pi.getPacketData(), 0,
1206 pi.getPacketData().length);
1207 counterStore.updatePacketInCounters(sw, m, eth);
1208 }
1209 // fall through to default case...
1210
1211 default:
1212
1213 List<IOFMessageListener> listeners = null;
1214 if (messageListeners.containsKey(m.getType())) {
1215 listeners = messageListeners.get(m.getType()).
1216 getOrderedListeners();
1217 }
1218
1219 FloodlightContext bc = null;
1220 if (listeners != null) {
1221 // Check if floodlight context is passed from the calling
1222 // function, if so use that floodlight context, otherwise
1223 // allocate one
1224 if (bContext == null) {
1225 bc = flcontext_alloc();
1226 } else {
1227 bc = bContext;
1228 }
1229 if (eth != null) {
1230 IFloodlightProviderService.bcStore.put(bc,
1231 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
1232 eth);
1233 }
1234
1235 // Get the starting time (overall and per-component) of
1236 // the processing chain for this packet if performance
1237 // monitoring is turned on
mininet73e7fb72013-12-03 14:25:53 -08001238
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001239 Command cmd;
1240 for (IOFMessageListener listener : listeners) {
1241 if (listener instanceof IOFSwitchFilter) {
1242 if (!((IOFSwitchFilter)listener).isInterested(sw)) {
1243 continue;
1244 }
1245 }
1246
mininet73e7fb72013-12-03 14:25:53 -08001247
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001248 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001249
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001250
1251 if (Command.STOP.equals(cmd)) {
1252 break;
1253 }
1254 }
mininet73e7fb72013-12-03 14:25:53 -08001255
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001256 } else {
1257 log.warn("Unhandled OF Message: {} from {}", m, sw);
1258 }
1259
1260 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1261 }
1262 }
1263
1264 /**
1265 * Log an OpenFlow error message from a switch
1266 * @param sw The switch that sent the error
1267 * @param error The error message
1268 */
1269 @LogMessageDoc(level="ERROR",
1270 message="Error {error type} {error code} from {switch}",
1271 explanation="The switch responded with an unexpected error" +
1272 "to an OpenFlow message from the controller",
1273 recommendation="This could indicate improper network operation. " +
1274 "If the problem persists restarting the switch and " +
1275 "controller may help."
1276 )
1277 protected void logError(IOFSwitch sw, OFError error) {
1278 int etint = 0xffff & error.getErrorType();
1279 if (etint < 0 || etint >= OFErrorType.values().length) {
1280 log.error("Unknown error code {} from sw {}", etint, sw);
1281 }
1282 OFErrorType et = OFErrorType.values()[etint];
1283 switch (et) {
1284 case OFPET_HELLO_FAILED:
1285 OFHelloFailedCode hfc =
1286 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1287 log.error("Error {} {} from {}", new Object[] {et, hfc, sw});
1288 break;
1289 case OFPET_BAD_REQUEST:
1290 OFBadRequestCode brc =
1291 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1292 log.error("Error {} {} from {}", new Object[] {et, brc, sw});
1293 break;
1294 case OFPET_BAD_ACTION:
1295 OFBadActionCode bac =
1296 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1297 log.error("Error {} {} from {}", new Object[] {et, bac, sw});
1298 break;
1299 case OFPET_FLOW_MOD_FAILED:
1300 OFFlowModFailedCode fmfc =
1301 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1302 log.error("Error {} {} from {}", new Object[] {et, fmfc, sw});
1303 break;
1304 case OFPET_PORT_MOD_FAILED:
1305 OFPortModFailedCode pmfc =
1306 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1307 log.error("Error {} {} from {}", new Object[] {et, pmfc, sw});
1308 break;
1309 case OFPET_QUEUE_OP_FAILED:
1310 OFQueueOpFailedCode qofc =
1311 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1312 log.error("Error {} {} from {}", new Object[] {et, qofc, sw});
1313 break;
1314 default:
1315 break;
1316 }
1317 }
1318
1319 /**
1320 * Add a switch to the active switch list and call the switch listeners.
1321 * This happens either when a switch first connects (and the controller is
1322 * not in the slave role) or when the role of the controller changes from
1323 * slave to master.
1324 * @param sw the switch that has been added
1325 */
1326 // TODO: need to rethink locking and the synchronous switch update.
1327 // We can / should also handle duplicate DPIDs in connectedSwitches
1328 @LogMessageDoc(level="ERROR",
1329 message="New switch added {switch} for already-added switch {switch}",
1330 explanation="A switch with the same DPID as another switch " +
1331 "connected to the controller. This can be caused by " +
1332 "multiple switches configured with the same DPID, or " +
1333 "by a switch reconnected very quickly after " +
1334 "disconnecting.",
1335 recommendation="If this happens repeatedly, it is likely there " +
1336 "are switches with duplicate DPIDs on the network. " +
1337 "Reconfigure the appropriate switches. If it happens " +
1338 "very rarely, then it is likely this is a transient " +
1339 "network problem that can be ignored."
1340 )
1341 protected void addSwitch(IOFSwitch sw) {
Jonathan Hart8a5d0972013-12-04 10:02:44 -08001342 // XXX Workaround to prevent race condition where a link is detected
1343 // and attempted to be written to the database before the port is in
1344 // the database. We now suppress link discovery on ports until we're
1345 // sure they're in the database.
1346 for (OFPhysicalPort port : sw.getPorts()) {
1347 linkDiscovery.AddToSuppressLLDPs(sw.getId(), port.getPortNumber());
1348 }
1349
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001350 // TODO: is it safe to modify the HashMap without holding
1351 // the old switch's lock?
1352 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1353 if (sw == oldSw) {
1354 // Note == for object equality, not .equals for value
1355 log.info("New add switch for pre-existing switch {}", sw);
1356 return;
1357 }
1358
1359 if (oldSw != null) {
1360 oldSw.getListenerWriteLock().lock();
1361 try {
1362 log.error("New switch added {} for already-added switch {}",
1363 sw, oldSw);
1364 // Set the connected flag to false to suppress calling
1365 // the listeners for this switch in processOFMessage
1366 oldSw.setConnected(false);
1367
1368 oldSw.cancelAllStatisticsReplies();
1369
Jonathan Hart2fa28062013-11-25 20:16:28 -08001370 //updateInactiveSwitchInfo(oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001371
1372 // we need to clean out old switch state definitively
1373 // before adding the new switch
1374 // FIXME: It seems not completely kosher to call the
1375 // switch listeners here. I thought one of the points of
1376 // having the asynchronous switch update mechanism was so
1377 // the addedSwitch and removedSwitch were always called
1378 // from a single thread to simplify concurrency issues
1379 // for the listener.
1380 if (switchListeners != null) {
1381 for (IOFSwitchListener listener : switchListeners) {
1382 listener.removedSwitch(oldSw);
1383 }
1384 }
1385 // will eventually trigger a removeSwitch(), which will cause
1386 // a "Not removing Switch ... already removed debug message.
1387 // TODO: Figure out a way to handle this that avoids the
1388 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001389 log.debug("Closing {} because a new IOFSwitch got added " +
1390 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001391 oldSw.getChannel().close();
1392 }
1393 finally {
1394 oldSw.getListenerWriteLock().unlock();
1395 }
1396 }
1397
Jonathan Hart2fa28062013-11-25 20:16:28 -08001398 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001399 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1400 try {
1401 this.updates.put(update);
1402 } catch (InterruptedException e) {
1403 log.error("Failure adding update to queue", e);
1404 }
1405 }
1406
1407 /**
1408 * Remove a switch from the active switch list and call the switch listeners.
1409 * This happens either when the switch is disconnected or when the
1410 * controller's role for the switch changes from master to slave.
1411 * @param sw the switch that has been removed
1412 */
1413 protected void removeSwitch(IOFSwitch sw) {
1414 // No need to acquire the listener lock, since
1415 // this method is only called after netty has processed all
1416 // pending messages
1417 log.debug("removeSwitch: {}", sw);
1418 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1419 log.debug("Not removing switch {}; already removed", sw);
1420 return;
1421 }
1422 // We cancel all outstanding statistics replies if the switch transition
1423 // from active. In the future we might allow statistics requests
1424 // from slave controllers. Then we need to move this cancelation
1425 // to switch disconnect
1426 sw.cancelAllStatisticsReplies();
1427
1428 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1429 // here if role support is enabled. In that case if the switch is being
1430 // removed because we've been switched to being in the slave role, then I think
1431 // it's possible that the new master may have already been promoted to master
1432 // and written out the active switch state to storage. If we now execute
1433 // updateInactiveSwitchInfo we may wipe out all of the state that was
1434 // written out by the new master. Maybe need to revisit how we handle all
1435 // of the switch state that's written to storage.
1436
Jonathan Hart2fa28062013-11-25 20:16:28 -08001437 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001438 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1439 try {
1440 this.updates.put(update);
1441 } catch (InterruptedException e) {
1442 log.error("Failure adding update to queue", e);
1443 }
1444 }
1445
1446 // ***************
1447 // IFloodlightProvider
1448 // ***************
1449
1450 @Override
1451 public synchronized void addOFMessageListener(OFType type,
1452 IOFMessageListener listener) {
1453 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1454 messageListeners.get(type);
1455 if (ldd == null) {
1456 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1457 messageListeners.put(type, ldd);
1458 }
1459 ldd.addListener(type, listener);
1460 }
1461
1462 @Override
1463 public synchronized void removeOFMessageListener(OFType type,
1464 IOFMessageListener listener) {
1465 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1466 messageListeners.get(type);
1467 if (ldd != null) {
1468 ldd.removeListener(listener);
1469 }
1470 }
1471
1472 private void logListeners() {
1473 for (Map.Entry<OFType,
1474 ListenerDispatcher<OFType,
1475 IOFMessageListener>> entry
1476 : messageListeners.entrySet()) {
1477
1478 OFType type = entry.getKey();
1479 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1480 entry.getValue();
1481
1482 StringBuffer sb = new StringBuffer();
1483 sb.append("OFListeners for ");
1484 sb.append(type);
1485 sb.append(": ");
1486 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1487 sb.append(l.getName());
1488 sb.append(",");
1489 }
1490 log.debug(sb.toString());
1491 }
1492 }
1493
1494 public void removeOFMessageListeners(OFType type) {
1495 messageListeners.remove(type);
1496 }
1497
1498 @Override
1499 public Map<Long, IOFSwitch> getSwitches() {
1500 return Collections.unmodifiableMap(this.activeSwitches);
1501 }
1502
1503 @Override
1504 public void addOFSwitchListener(IOFSwitchListener listener) {
1505 this.switchListeners.add(listener);
1506 }
1507
1508 @Override
1509 public void removeOFSwitchListener(IOFSwitchListener listener) {
1510 this.switchListeners.remove(listener);
1511 }
1512
1513 @Override
1514 public Map<OFType, List<IOFMessageListener>> getListeners() {
1515 Map<OFType, List<IOFMessageListener>> lers =
1516 new HashMap<OFType, List<IOFMessageListener>>();
1517 for(Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1518 messageListeners.entrySet()) {
1519 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1520 }
1521 return Collections.unmodifiableMap(lers);
1522 }
1523
1524 @Override
1525 @LogMessageDocs({
1526 @LogMessageDoc(message="Failed to inject OFMessage {message} onto " +
1527 "a null switch",
1528 explanation="Failed to process a message because the switch " +
1529 " is no longer connected."),
1530 @LogMessageDoc(level="ERROR",
1531 message="Error reinjecting OFMessage on switch {switch}",
1532 explanation="An I/O error occured while attempting to " +
1533 "process an OpenFlow message",
1534 recommendation=LogMessageDoc.CHECK_SWITCH)
1535 })
1536 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1537 FloodlightContext bc) {
1538 if (sw == null) {
1539 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1540 return false;
1541 }
1542
1543 // FIXME: Do we need to be able to inject messages to switches
1544 // where we're the slave controller (i.e. they're connected but
1545 // not active)?
1546 // FIXME: Don't we need synchronization logic here so we're holding
1547 // the listener read lock when we call handleMessage? After some
1548 // discussions it sounds like the right thing to do here would be to
1549 // inject the message as a netty upstream channel event so it goes
1550 // through the normal netty event processing, including being
1551 // handled
1552 if (!activeSwitches.containsKey(sw.getId())) return false;
1553
1554 try {
1555 // Pass Floodlight context to the handleMessages()
1556 handleMessage(sw, msg, bc);
1557 } catch (IOException e) {
1558 log.error("Error reinjecting OFMessage on switch {}",
1559 HexString.toHexString(sw.getId()));
1560 return false;
1561 }
1562 return true;
1563 }
1564
1565 @Override
1566 @LogMessageDoc(message="Calling System.exit",
1567 explanation="The controller is terminating")
1568 public synchronized void terminate() {
1569 log.info("Calling System.exit");
1570 System.exit(1);
1571 }
1572
1573 @Override
1574 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1575 // call the overloaded version with floodlight context set to null
1576 return injectOfMessage(sw, msg, null);
1577 }
1578
1579 @Override
1580 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1581 FloodlightContext bc) {
1582 if (log.isTraceEnabled()) {
1583 String str = OFMessage.getDataAsString(sw, m, bc);
1584 log.trace("{}", str);
1585 }
1586
1587 List<IOFMessageListener> listeners = null;
1588 if (messageListeners.containsKey(m.getType())) {
1589 listeners =
1590 messageListeners.get(m.getType()).getOrderedListeners();
1591 }
1592
1593 if (listeners != null) {
1594 for (IOFMessageListener listener : listeners) {
1595 if (listener instanceof IOFSwitchFilter) {
1596 if (!((IOFSwitchFilter)listener).isInterested(sw)) {
1597 continue;
1598 }
1599 }
1600 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1601 break;
1602 }
1603 }
1604 }
1605 }
1606
1607 @Override
1608 public BasicFactory getOFMessageFactory() {
1609 return factory;
1610 }
1611
1612 @Override
1613 public String getControllerId() {
1614 return controllerId;
1615 }
1616
1617 // **************
1618 // Initialization
1619 // **************
1620
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001621 /**
1622 * Sets the initial role based on properties in the config params.
1623 * It looks for two different properties.
1624 * If the "role" property is specified then the value should be
1625 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1626 * controller is set to the specified value. If the "role" property
1627 * is not specified then it looks next for the "role.path" property.
1628 * In this case the value should be the path to a property file in
1629 * the file system that contains a property called "floodlight.role"
1630 * which can be one of the values listed above for the "role" property.
1631 * The idea behind the "role.path" mechanism is that you have some
1632 * separate heartbeat and master controller election algorithm that
1633 * determines the role of the controller. When a role transition happens,
1634 * it updates the current role in the file specified by the "role.path"
1635 * file. Then if floodlight restarts for some reason it can get the
1636 * correct current role of the controller from the file.
1637 * @param configParams The config params for the FloodlightProvider service
1638 * @return A valid role if role information is specified in the
1639 * config params, otherwise null
1640 */
1641 @LogMessageDocs({
1642 @LogMessageDoc(message="Controller role set to {role}",
1643 explanation="Setting the initial HA role to "),
1644 @LogMessageDoc(level="ERROR",
1645 message="Invalid current role value: {role}",
1646 explanation="An invalid HA role value was read from the " +
1647 "properties file",
1648 recommendation=LogMessageDoc.CHECK_CONTROLLER)
1649 })
1650 protected Role getInitialRole(Map<String, String> configParams) {
1651 Role role = null;
1652 String roleString = configParams.get("role");
1653 if (roleString == null) {
1654 String rolePath = configParams.get("rolepath");
1655 if (rolePath != null) {
1656 Properties properties = new Properties();
1657 try {
1658 properties.load(new FileInputStream(rolePath));
1659 roleString = properties.getProperty("floodlight.role");
1660 }
1661 catch (IOException exc) {
1662 // Don't treat it as an error if the file specified by the
1663 // rolepath property doesn't exist. This lets us enable the
1664 // HA mechanism by just creating/setting the floodlight.role
1665 // property in that file without having to modify the
1666 // floodlight properties.
1667 }
1668 }
1669 }
1670
1671 if (roleString != null) {
1672 // Canonicalize the string to the form used for the enum constants
1673 roleString = roleString.trim().toUpperCase();
1674 try {
1675 role = Role.valueOf(roleString);
1676 }
1677 catch (IllegalArgumentException exc) {
1678 log.error("Invalid current role value: {}", roleString);
1679 }
1680 }
1681
1682 log.info("Controller role set to {}", role);
1683
1684 return role;
1685 }
1686
1687 /**
1688 * Tell controller that we're ready to accept switches loop
1689 * @throws IOException
1690 */
1691 @LogMessageDocs({
1692 @LogMessageDoc(message="Listening for switch connections on {address}",
1693 explanation="The controller is ready and listening for new" +
1694 " switch connections"),
1695 @LogMessageDoc(message="Storage exception in controller " +
1696 "updates loop; terminating process",
1697 explanation=ERROR_DATABASE,
1698 recommendation=LogMessageDoc.CHECK_CONTROLLER),
1699 @LogMessageDoc(level="ERROR",
1700 message="Exception in controller updates loop",
1701 explanation="Failed to dispatch controller event",
1702 recommendation=LogMessageDoc.GENERIC_ACTION)
1703 })
1704 public void run() {
1705 if (log.isDebugEnabled()) {
1706 logListeners();
1707 }
1708
1709 try {
1710 final ServerBootstrap bootstrap = createServerBootStrap();
1711
1712 bootstrap.setOption("reuseAddr", true);
1713 bootstrap.setOption("child.keepAlive", true);
1714 bootstrap.setOption("child.tcpNoDelay", true);
1715 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1716
1717 ChannelPipelineFactory pfact =
1718 new OpenflowPipelineFactory(this, null);
1719 bootstrap.setPipelineFactory(pfact);
1720 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1721 final ChannelGroup cg = new DefaultChannelGroup();
1722 cg.add(bootstrap.bind(sa));
1723
1724 log.info("Listening for switch connections on {}", sa);
1725 } catch (Exception e) {
1726 throw new RuntimeException(e);
1727 }
1728
1729 // main loop
1730 while (true) {
1731 try {
1732 IUpdate update = updates.take();
1733 update.dispatch();
1734 } catch (InterruptedException e) {
1735 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001736 } catch (Exception e) {
1737 log.error("Exception in controller updates loop", e);
1738 }
1739 }
1740 }
1741
1742 private ServerBootstrap createServerBootStrap() {
1743 if (workerThreads == 0) {
1744 return new ServerBootstrap(
1745 new NioServerSocketChannelFactory(
1746 Executors.newCachedThreadPool(),
1747 Executors.newCachedThreadPool()));
1748 } else {
1749 return new ServerBootstrap(
1750 new NioServerSocketChannelFactory(
1751 Executors.newCachedThreadPool(),
1752 Executors.newCachedThreadPool(), workerThreads));
1753 }
1754 }
1755
1756 public void setConfigParams(Map<String, String> configParams) {
1757 String ofPort = configParams.get("openflowport");
1758 if (ofPort != null) {
1759 this.openFlowPort = Integer.parseInt(ofPort);
1760 }
1761 log.debug("OpenFlow port set to {}", this.openFlowPort);
1762 String threads = configParams.get("workerthreads");
1763 if (threads != null) {
1764 this.workerThreads = Integer.parseInt(threads);
1765 }
1766 log.debug("Number of worker threads set to {}", this.workerThreads);
1767 String controllerId = configParams.get("controllerid");
1768 if (controllerId != null) {
1769 this.controllerId = controllerId;
1770 }
Jonathan Hartd10008d2013-02-23 17:04:08 -08001771 else {
1772 //Try to get the hostname of the machine and use that for controller ID
1773 try {
1774 String hostname = java.net.InetAddress.getLocalHost().getHostName();
1775 this.controllerId = hostname;
1776 } catch (UnknownHostException e) {
1777 // Can't get hostname, we'll just use the default
1778 }
1779 }
1780
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001781 log.debug("ControllerId set to {}", this.controllerId);
1782 }
1783
1784 private void initVendorMessages() {
1785 // Configure openflowj to be able to parse the role request/reply
1786 // vendor messages.
1787 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1788 OFNiciraVendorData.NX_VENDOR_ID, 4);
1789 OFVendorId.registerVendorId(niciraVendorId);
1790 OFBasicVendorDataType roleRequestVendorData =
1791 new OFBasicVendorDataType(
1792 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1793 OFRoleRequestVendorData.getInstantiable());
1794 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1795 OFBasicVendorDataType roleReplyVendorData =
1796 new OFBasicVendorDataType(
1797 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1798 OFRoleReplyVendorData.getInstantiable());
1799 niciraVendorId.registerVendorDataType(roleReplyVendorData);
1800 }
1801
1802 /**
1803 * Initialize internal data structures
1804 */
1805 public void init(Map<String, String> configParams) {
1806 // These data structures are initialized here because other
1807 // module's startUp() might be called before ours
1808 this.messageListeners =
1809 new ConcurrentHashMap<OFType,
1810 ListenerDispatcher<OFType,
1811 IOFMessageListener>>();
1812 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001813 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1814 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1815 this.controllerNodeIPsCache = new HashMap<String, String>();
1816 this.updates = new LinkedBlockingQueue<IUpdate>();
1817 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001818 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001819 //Set the controller's role to MASTER so it always tries to do role requests.
1820 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001821 this.roleChanger = new RoleChanger();
1822 initVendorMessages();
1823 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001824 }
1825
1826 /**
1827 * Startup all of the controller's components
1828 */
1829 @LogMessageDoc(message="Waiting for storage source",
1830 explanation="The system database is not yet ready",
1831 recommendation="If this message persists, this indicates " +
1832 "that the system database has failed to start. " +
1833 LogMessageDoc.CHECK_CONTROLLER)
1834 public void startupComponents() {
Jonathan Hartd10008d2013-02-23 17:04:08 -08001835 try {
1836 registryService.registerController(controllerId);
Jonathan Hartb0904bf2013-11-26 14:41:11 -08001837 } catch (RegistryException e) {
1838 log.warn("Registry service error: {}", e.getMessage());
Jonathan Hartd10008d2013-02-23 17:04:08 -08001839 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001840
1841 // Add our REST API
1842 restApi.addRestletRoutable(new CoreWebRoutable());
1843 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001844
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001845 @Override
1846 public Map<String, String> getControllerNodeIPs() {
1847 // We return a copy of the mapping so we can guarantee that
1848 // the mapping return is the same as one that will be (or was)
1849 // dispatched to IHAListeners
1850 HashMap<String,String> retval = new HashMap<String,String>();
1851 synchronized(controllerNodeIPsCache) {
1852 retval.putAll(controllerNodeIPsCache);
1853 }
1854 return retval;
1855 }
1856
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001857 @Override
1858 public long getSystemStartTime() {
1859 return (this.systemStartTime);
1860 }
1861
1862 @Override
1863 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1864 this.alwaysClearFlowsOnSwAdd = value;
1865 }
1866
1867 public boolean getAlwaysClearFlowsOnSwAdd() {
1868 return this.alwaysClearFlowsOnSwAdd;
1869 }
1870}