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