blob: b47962f44afc9c0837a8646b13c7517842513c70 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080017
18package net.floodlightcontroller.core.internal;
19
20import java.io.FileInputStream;
21import java.io.IOException;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070022import java.lang.management.ManagementFactory;
23import java.lang.management.RuntimeMXBean;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080024import java.net.InetSocketAddress;
Jonathan Hartd10008d2013-02-23 17:04:08 -080025import java.net.UnknownHostException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080026import java.util.Collections;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080027import java.util.HashMap;
28import java.util.HashSet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080029import java.util.List;
30import java.util.Map;
31import java.util.Map.Entry;
32import java.util.Properties;
33import java.util.Set;
34import java.util.Stack;
35import java.util.concurrent.BlockingQueue;
36import java.util.concurrent.ConcurrentHashMap;
37import java.util.concurrent.ConcurrentMap;
38import java.util.concurrent.CopyOnWriteArraySet;
39import java.util.concurrent.Executors;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080040import java.util.concurrent.LinkedBlockingQueue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041
42import net.floodlightcontroller.core.FloodlightContext;
43import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080044import net.floodlightcontroller.core.IListener.Command;
Jonathan Hartd10008d2013-02-23 17:04:08 -080045import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080046import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070047import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080048import net.floodlightcontroller.core.IOFSwitchFilter;
49import net.floodlightcontroller.core.IOFSwitchListener;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070050import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080051import net.floodlightcontroller.core.annotations.LogMessageDoc;
52import net.floodlightcontroller.core.annotations.LogMessageDocs;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070053import net.floodlightcontroller.core.internal.OFChannelHandler.RoleRecvStatus;
54import net.floodlightcontroller.core.module.FloodlightModuleException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080055import net.floodlightcontroller.core.util.ListenerDispatcher;
56import net.floodlightcontroller.core.web.CoreWebRoutable;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070057import net.floodlightcontroller.debugcounter.IDebugCounter;
58import net.floodlightcontroller.debugcounter.IDebugCounterService;
59import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
60import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
61import net.floodlightcontroller.debugevent.IDebugEventService;
62import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
63import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
64import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
65import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered;
66import net.floodlightcontroller.debugevent.IEventUpdater;
67import net.floodlightcontroller.debugevent.NullDebugEvent;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080068import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080069import net.floodlightcontroller.threadpool.IThreadPoolService;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070070import net.floodlightcontroller.util.LoadMonitor;
71import net.onrc.onos.core.drivermanager.DriverManager;
Jonathan Hart23701d12014-04-03 10:45:48 -070072import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070073import net.onrc.onos.core.packet.Ethernet;
74import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070075import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070076import net.onrc.onos.core.registry.RegistryException;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070077import net.onrc.onos.core.util.Dpid;
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -070078import net.onrc.onos.core.util.OnosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080079
80import org.jboss.netty.bootstrap.ServerBootstrap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080081import org.jboss.netty.channel.ChannelPipelineFactory;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080082import org.jboss.netty.channel.group.ChannelGroup;
83import org.jboss.netty.channel.group.DefaultChannelGroup;
84import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070085import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
86import org.projectfloodlight.openflow.protocol.OFFactories;
87import org.projectfloodlight.openflow.protocol.OFFactory;
88import org.projectfloodlight.openflow.protocol.OFMessage;
89import org.projectfloodlight.openflow.protocol.OFPacketIn;
90import org.projectfloodlight.openflow.protocol.OFPortDesc;
91import org.projectfloodlight.openflow.protocol.OFType;
92import org.projectfloodlight.openflow.protocol.OFVersion;
93import org.projectfloodlight.openflow.protocol.match.MatchField;
94import org.projectfloodlight.openflow.protocol.match.MatchFields;
95import org.projectfloodlight.openflow.types.EthType;
96import org.projectfloodlight.openflow.types.OFPort;
97import org.projectfloodlight.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080098import org.slf4j.Logger;
99import org.slf4j.LoggerFactory;
100
101
102/**
103 * The main controller class. Handles all setup and network listeners
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700104 * - Distributed ownership control of switch through IControllerRegistryService
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800105 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800106public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700107
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700108 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700109 static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800110 "The controller could not communicate with the system database.";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700111 protected static OFFactory factory13 = OFFactories.getFactory(OFVersion.OF_13);
112 protected static OFFactory factory10 = OFFactories.getFactory(OFVersion.OF_10);
Ray Milkey269ffb92014-04-03 14:43:30 -0700113
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700114 // connectedSwitches cache contains all connected switch's channelHandlers
115 // including ones where this controller is a master/equal/slave controller
116 // as well as ones that have not been activated yet
117 protected ConcurrentHashMap<Long, OFChannelHandler> connectedSwitches;
118 // These caches contains only those switches that are active
119 protected ConcurrentHashMap<Long, IOFSwitch> activeMasterSwitches;
120 protected ConcurrentHashMap<Long, IOFSwitch> activeEqualSwitches;
121 // lock to synchronize on, when manipulating multiple caches above
122 private Object multiCacheLock;
Ray Milkey269ffb92014-04-03 14:43:30 -0700123
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700124 // The controllerNodeIPsCache maps Controller IDs to their IP address.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800125 // It's only used by handleControllerNodeIPsChanged
126 protected HashMap<String, String> controllerNodeIPsCache;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800127 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700128
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700129 protected ConcurrentMap<OFType,
130 ListenerDispatcher<OFType, IOFMessageListener>> messageListeners;
131 protected Set<IOFSwitchListener> switchListeners;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700132
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800133 // Module dependencies
134 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800135 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800136 protected IControllerRegistryService registryService;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700137 protected IDebugCounterService debugCounters;
138 protected IDebugEventService debugEvents;
Ray Milkey269ffb92014-04-03 14:43:30 -0700139
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800140 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700141
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800142 // Configuration options
143 protected int openFlowPort = 6633;
144 protected int workerThreads = 0;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700145
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800146 // The id for this controller node. Should be unique for each controller
147 // node in a controller cluster.
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -0700148 private OnosInstanceId onosInstanceId = new OnosInstanceId("localhost");
Ray Milkey269ffb92014-04-03 14:43:30 -0700149
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700150 // defined counters
151 private Counters counters;
152 // Event IDs for debug events
153 protected IEventUpdater<SwitchEvent> evSwitch;
154
155 // Load monitor for overload protection
156 protected final boolean overload_drop =
157 Boolean.parseBoolean(System.getProperty("overload_drop", "false"));
158 protected final LoadMonitor loadmonitor = new LoadMonitor(log);
Ray Milkey269ffb92014-04-03 14:43:30 -0700159
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800160 // Start time of the controller
161 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700162
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800163 // Flag to always flush flow table on switch reconnect (HA or otherwise)
164 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700165
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800166 // Perf. related configuration
167 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
168 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700169 protected static final boolean ALWAYS_DECODE_ETH = true;
170
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700171
172
173 // ******************************
174 // Switch Management and Updates
175 // ******************************
176
177 /**
178 * Switch updates are sent to all IOFSwitchListeners. A switch that is connected
179 * to this controller instance, but not activated, is not available for updates.
180 *
181 * In ONOS, each controller instance can simultaneously serve in a MASTER role
182 * for some connected switches, and in a EQUAL role for other connected switches.
183 * The EQUAL role can be treated as a SLAVE role, by ensuring that the
184 * controller instance never sends packets or commands out to the switch.
185 * Activated switches, either with Controller Role MASTER or EQUAL are announced
186 * as updates. We also support announcements of controller role transitions
187 * from MASTER --> EQUAL, and EQUAL --> MASTER, for an individual switch.
188 *
189 * Disconnection of only activated switches are announced. Finally, changes
190 * to switch ports are announced with a portChangeType (see @IOFSwitch)
191 *
192 * @author saurav
193 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800194 public enum SwitchUpdateType {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700195 /** switch activated with this controller's role as MASTER */
196 ACTIVATED_MASTER,
197 /** switch activated with this controller's role as EQUAL.
198 * listener can treat this controller's role as SLAVE by not
199 * sending packets or commands to the switch */
200 ACTIVATED_EQUAL,
201 /** this controller's role for this switch changed from Master to Equal */
202 MASTER_TO_EQUAL,
203 /** this controller's role for this switch changed form Equal to Master */
204 EQUAL_TO_MASTER,
205 /** A previously activated switch disconnected */
206 DISCONNECTED,
207 /** Port changed on a previously activated switch */
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700208 PORTCHANGED,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800209 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700210
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700213 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800214 */
215 protected class SwitchUpdate implements IUpdate {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700216 public long getSwId() {
217 return swId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800218 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700219
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700220 public SwitchUpdateType getSwitchUpdateType() {
221 return switchUpdateType;
222 }
223
224 public PortChangeType getPortChangeType() {
225 return changeType;
226 }
227
228 private final long swId;
229 private final SwitchUpdateType switchUpdateType;
230 private final OFPortDesc port;
231 private final PortChangeType changeType;
232
233 public SwitchUpdate(long swId, SwitchUpdateType switchUpdateType) {
234 this(swId, switchUpdateType, null, null);
235 }
236 public SwitchUpdate(long swId,
237 SwitchUpdateType switchUpdateType,
238 OFPortDesc port,
239 PortChangeType changeType) {
240 if (switchUpdateType == SwitchUpdateType.PORTCHANGED) {
241 if (port == null) {
242 throw new NullPointerException("Port must not be null " +
243 "for PORTCHANGED updates");
244 }
245 if (changeType == null) {
246 throw new NullPointerException("ChangeType must not be " +
247 "null for PORTCHANGED updates");
248 }
249 } else {
250 if (port != null || changeType != null) {
251 throw new IllegalArgumentException("port and changeType " +
252 "must be null for " + switchUpdateType +
253 " updates");
254 }
255 }
256 this.swId = swId;
257 this.switchUpdateType = switchUpdateType;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700258 this.port = port;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700259 this.changeType = changeType;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700260 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700261
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700262 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800263 public void dispatch() {
264 if (log.isTraceEnabled()) {
265 log.trace("Dispatching switch update {} {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700266 HexString.toHexString(swId), switchUpdateType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800267 }
268 if (switchListeners != null) {
269 for (IOFSwitchListener listener : switchListeners) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700270 switch(switchUpdateType) {
271 case ACTIVATED_MASTER:
272 // don't count here. We have more specific
273 // counters before the update is created
274 listener.switchActivatedMaster(swId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800275 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700276 case ACTIVATED_EQUAL:
277 // don't count here. We have more specific
278 // counters before the update is created
279 listener.switchActivatedEqual(swId);
280 break;
281 case MASTER_TO_EQUAL:
282 listener.switchMasterToEqual(swId);
283 break;
284 case EQUAL_TO_MASTER:
285 listener.switchEqualToMaster(swId);
286 break;
287 case DISCONNECTED:
288 // don't count here. We have more specific
289 // counters before the update is created
290 listener.switchDisconnected(swId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291 break;
292 case PORTCHANGED:
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700293 counters.switchPortChanged.updateCounterWithFlush();
294 listener.switchPortChanged(swId, port, changeType);
Ray Milkey269ffb92014-04-03 14:43:30 -0700295 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800296 }
297 }
298 }
299 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700300
301
302 }
303
304 protected boolean addConnectedSwitch(long dpid, OFChannelHandler h) {
305 if (connectedSwitches.get(dpid) != null) {
306 log.error("Trying to add connectedSwitch but found a previous "
307 + "value for dpid: {}", dpid);
308 return false;
309 } else {
310 connectedSwitches.put(dpid, h);
311 return true;
312 }
313 }
314
315 /**
316 * Switch Events
317 */
318 @Override
319 public void addSwitchEvent(long dpid, String reason, boolean flushNow) {
320 if (flushNow)
321 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, reason));
322 else
323 evSwitch.updateEventNoFlush(new SwitchEvent(dpid, reason));
324 }
325
326 private boolean validActivation(long dpid) {
327 if (connectedSwitches.get(dpid) == null) {
328 log.error("Trying to activate switch but is not in "
329 + "connected switches: dpid {}. Aborting ..",
330 HexString.toHexString(dpid));
331 return false;
332 }
333 if (activeMasterSwitches.get(dpid) != null ||
334 activeEqualSwitches.get(dpid) != null) {
335 log.error("Trying to activate switch but it is already "
336 + "activated: dpid {}. Found in activeMaster: {} "
337 + "Found in activeEqual: {}. Aborting ..", new Object[] {
338 HexString.toHexString(dpid),
339 (activeMasterSwitches.get(dpid) == null)? 'Y': 'N',
340 (activeEqualSwitches.get(dpid) == null)? 'Y': 'N'});
341 counters.switchWithSameDpidActivated.updateCounterWithFlush();
342 return false;
343 }
344 return true;
345 }
346
347 /**
348 * Called when a switch is activated, with this controller's role as MASTER
349 */
350 protected boolean addActivatedMasterSwitch(long dpid, IOFSwitch sw) {
351 synchronized(multiCacheLock) {
352 if (!validActivation(dpid)) return false;
353 activeMasterSwitches.put(dpid, sw);
354 }
355 // XXX Workaround to prevent race condition where a link is detected
356 // and attempted to be written to the database before the port is in
357 // the database. We now suppress link discovery on ports until we're
358 // sure they're in the database.
359 for (OFPortDesc port : sw.getPorts()) {
360 linkDiscovery.disableDiscoveryOnPort(sw.getId(),
361 port.getPortNo().getShortPortNumber());
362 }
363 //update counters and events
364 counters.switchActivated.updateCounterWithFlush();
365 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "activeMaster"));
366 addUpdateToQueue(new SwitchUpdate(dpid,
367 SwitchUpdateType.ACTIVATED_MASTER));
368 return true;
369 }
370
371 /**
372 * Called when a switch is activated, with this controller's role as EQUAL
373 */
374 protected boolean addActivatedEqualSwitch(long dpid, IOFSwitch sw) {
375 synchronized(multiCacheLock) {
376 if (!validActivation(dpid)) return false;
377 activeEqualSwitches.put(dpid, sw);
378 }
379 //update counters and events
380 counters.switchActivated.updateCounterWithFlush();
381 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "activeEqual"));
382 addUpdateToQueue(new SwitchUpdate(dpid,
383 SwitchUpdateType.ACTIVATED_EQUAL));
384 return true;
385 }
386
387 /**
388 * Called when this controller's role for a switch transitions from equal
389 * to master. For 1.0 switches, we internally refer to the role 'slave' as
390 * 'equal' - so this transition is equivalent to 'addActivatedMasterSwitch'.
391 */
392 protected void transitionToMasterSwitch(long dpid) {
393 synchronized(multiCacheLock) {
394 IOFSwitch sw = activeEqualSwitches.remove(dpid);
395 if (sw == null) {
396 log.error("Transition to master called on sw {}, but switch "
397 + "was not found in controller-cache", dpid);
398 return;
399 }
400 activeMasterSwitches.put(dpid, sw);
401 }
402 addUpdateToQueue(new SwitchUpdate(dpid,
403 SwitchUpdateType.EQUAL_TO_MASTER));
404 }
405
406
407 /**
408 * Called when this controller's role for a switch transitions to equal.
409 * For 1.0 switches, we internally refer to the role 'slave' as
410 * 'equal'.
411 */
412 protected void transitionToEqualSwitch(long dpid) {
413 synchronized(multiCacheLock) {
414 IOFSwitch sw = activeMasterSwitches.remove(dpid);
415 if (sw == null) {
416 log.error("Transition to equal called on sw {}, but switch "
417 + "was not found in controller-cache", dpid);
418 return;
419 }
420 activeEqualSwitches.put(dpid, sw);
421 }
422 addUpdateToQueue(new SwitchUpdate(dpid,
423 SwitchUpdateType.MASTER_TO_EQUAL));
424 }
425
426 /**
427 * Clear all state in controller switch maps for a switch that has
428 * disconnected from the local controller. Also release control for
429 * that switch from the global repository. Notify switch listeners.
430 */
431 protected void removeConnectedSwitch(long dpid) {
432 releaseRegistryControl(dpid);
433 connectedSwitches.remove(dpid);
434 IOFSwitch sw = activeMasterSwitches.remove(dpid);
435 if (sw == null) sw = activeEqualSwitches.remove(dpid);
436 if (sw != null) {
437 sw.cancelAllStatisticsReplies();
438 sw.setConnected(false); // do we need this?
439 }
440 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "disconnected"));
441 counters.switchDisconnected.updateCounterWithFlush();
442 addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.DISCONNECTED));
443 }
444
445 /**
446 * Indicates that ports on the given switch have changed. Enqueue a
447 * switch update.
448 * @param sw
449 */
450 protected void notifyPortChanged(long dpid, OFPortDesc port,
451 PortChangeType changeType) {
452 if (port == null || changeType == null) {
453 String msg = String.format("Switch port or changetType must not "
454 + "be null in port change notification");
455 throw new NullPointerException(msg);
456 }
457 if (connectedSwitches.get(dpid) == null || getSwitch(dpid) == null) {
458 log.warn("Port change update on switch {} not connected or activated "
459 + "... Aborting.", HexString.toHexString(dpid));
460 return;
461 }
462
463 if (changeType == PortChangeType.ADD) {
464 // XXX Workaround to prevent race condition where a link is detected
465 // and attempted to be written to the database before the port is in
466 // the database. We now suppress link discovery on ports until we're
467 // sure they're in the database.
468 linkDiscovery.disableDiscoveryOnPort(dpid, port.getPortNo().getShortPortNumber());
469 }
470
471 SwitchUpdate update = new SwitchUpdate(dpid, SwitchUpdateType.PORTCHANGED,
472 port, changeType);
473 addUpdateToQueue(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800474 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700475
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800476 // ***************
477 // Getters/Setters
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700478 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700479
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800480 public void setRestApiService(IRestApiService restApi) {
481 this.restApi = restApi;
482 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700483
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800484 public void setThreadPoolService(IThreadPoolService tp) {
485 this.threadPool = tp;
486 }
487
Ray Milkey269ffb92014-04-03 14:43:30 -0700488 public void setMastershipService(IControllerRegistryService serviceImpl) {
489 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700490 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700491
492 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
493 this.linkDiscovery = linkDiscovery;
494 }
495
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700496 public void setDebugCounter(IDebugCounterService debugCounters) {
497 this.debugCounters = debugCounters;
498 }
499
500 public void setDebugEvent(IDebugEventService debugEvents) {
501 this.debugEvents = debugEvents;
502 }
503
504 IDebugCounterService getDebugCounter() {
505 return this.debugCounters;
Ray Milkey269ffb92014-04-03 14:43:30 -0700506 }
507
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800508 // **********************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700509 // Role Handling
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800510 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700511
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700512 /** created by ONOS - works with registry service
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800513 */
Jonathan Hartcc957a02013-02-26 10:39:04 -0800514 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700515 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700516 public void controlChanged(long dpidLong, boolean hasControl) {
517 Dpid dpid = new Dpid(dpidLong);
Ray Milkey269ffb92014-04-03 14:43:30 -0700518 log.info("Role change callback for switch {}, hasControl {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700519 dpid, hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800520
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800521 Role role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700522
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700523 /*
524 * issue #229
525 * Cannot rely on sw.getRole() as it can be behind due to pending
526 * role changes in the queue. Just submit it and late the
527 * RoleChanger handle duplicates.
528 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700529
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700530 if (hasControl) {
531 role = Role.MASTER;
Ray Milkey269ffb92014-04-03 14:43:30 -0700532 } else {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700533 role = Role.EQUAL; // treat the same as Role.SLAVE
Ray Milkey269ffb92014-04-03 14:43:30 -0700534 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700535
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700536 OFChannelHandler swCh = connectedSwitches.get(dpid.value());
537 if (swCh == null) {
538 log.warn("Switch {} not found in connected switches", dpid);
539 return;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700540 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700541
542 log.debug("Sending role request {} msg to {}", role, dpid);
543 swCh.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800544 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700545 }
546
547 public synchronized void submitRegistryRequest(long dpid) {
548 OFChannelHandler h = connectedSwitches.get(dpid);
549 if (h == null) {
550 log.error("Trying to request registry control for switch {} "
551 + "not in connected switches. Aborting.. ",
552 HexString.toHexString(dpid));
553 // FIXME shouldn't we immediately return here?
554 }
555 //Request control of the switch from the global registry
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800556 try {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700557 h.controlRequested = Boolean.TRUE;
558 registryService.requestControl(dpid, new RoleChangeCallback());
559 } catch (RegistryException e) {
560 log.debug("Registry error: {}", e.getMessage());
561 h.controlRequested = Boolean.FALSE;
562 }
563 if (!h.controlRequested) { // XXX what is being attempted here?
564 // yield to allow other thread(s) to release control
565 try {
566 Thread.sleep(10);
567 } catch (InterruptedException e) {
568 // Ignore interruptions
569 }
570 // safer to bounce the switch to reconnect here than proceeding further
571 // XXX S why? can't we just try again a little later?
572 log.debug("Closing sw:{} because we weren't able to request control " +
573 "successfully" + dpid);
574 connectedSwitches.get(dpid).disconnectSwitch();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800575 }
576 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700577
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700578 public synchronized void releaseRegistryControl(long dpidLong) {
579 OFChannelHandler h = connectedSwitches.get(dpidLong);
580 if (h == null) {
581 log.error("Trying to release registry control for switch {} "
582 + "not in connected switches. Aborting.. ",
583 HexString.toHexString(dpidLong));
584 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800585 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700586 if (h.controlRequested) {
587 registryService.releaseControl(dpidLong);
588 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 }
590
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700591
592 // *******************
593 // OF Message Handling
594 // *******************
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800595
596 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700597 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700598 * Handle and dispatch a message to IOFMessageListeners.
599 *
600 * We only dispatch messages to listeners if the controller's role is MASTER.
601 *
602 * @param sw The switch sending the message
603 * @param m The message the switch sent
604 * @param flContext The floodlight context to use for this message. If
605 * null, a new context will be allocated.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800606 * @throws IOException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700607 *
608 * FIXME: this method and the ChannelHandler disagree on which messages
609 * should be dispatched and which shouldn't
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800610 */
611 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700612 @LogMessageDoc(level = "ERROR",
613 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
614 " field is empty.",
615 explanation = "The switch sent an improperly-formatted PacketIn" +
616 " message",
617 recommendation = LogMessageDoc.CHECK_SWITCH),
618 @LogMessageDoc(level = "WARN",
619 message = "Unhandled OF Message: {} from {}",
620 explanation = "The switch sent a message not handled by " +
621 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800622 })
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700623 @SuppressWarnings({ "fallthrough", "unchecked" })
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800624 protected void handleMessage(IOFSwitch sw, OFMessage m,
625 FloodlightContext bContext)
626 throws IOException {
627 Ethernet eth = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700628 short inport = -1;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629
630 switch (m.getType()) {
631 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -0700632 OFPacketIn pi = (OFPacketIn) m;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700633 //log.info("saw packet in from sw {}", sw.getStringId());
634 if (pi.getData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700635 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700636 ") because/* the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800637 return;
638 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700639
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700640 // get incoming port to store in floodlight context
641 if (sw.getOFVersion() == OFVersion.OF_10) {
642 inport = pi.getInPort().getShortPortNumber();
643 } else if (sw.getOFVersion() == OFVersion.OF_13) {
644 for (MatchField<?> mf : pi.getMatch().getMatchFields()) {
645 if (mf.id == MatchFields.IN_PORT) {
646 inport = pi.getMatch().get((MatchField<OFPort>)mf)
647 .getShortPortNumber();
648 break;
649 }
650 }
651 if (inport == -1) {
652 log.error("Match field for incoming port missing in "
653 + "packet-in from sw {} .. Ignoring msg",
654 sw.getStringId());
655 return;
656 }
657 } else {
658 // should have been taken care of earlier in handshake
659 log.error("OFVersion {} not supported for "
660 + "packet-in from sw {} .. Ignoring msg",
661 sw.getOFVersion(), sw.getStringId());
662 return;
663 }
664
665 // decode enclosed ethernet packet to store in floodlight context
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800666 if (Controller.ALWAYS_DECODE_ETH) {
667 eth = new Ethernet();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700668 eth.deserialize(pi.getData(), 0,
669 pi.getData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800670 }
671 // fall through to default case...
672
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700673 /*log.debug("Sw:{} packet-in: {}", sw.getStringId(),
674 String.format("0x%x", eth.getEtherType()));*/
675 if (eth.getEtherType() != (short)EthType.LLDP.getValue())
676 log.trace("Sw:{} packet-in: {}", sw.getStringId(), pi);
677
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800678 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700679
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800680 List<IOFMessageListener> listeners = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700681
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800682 if (messageListeners.containsKey(m.getType())) {
683 listeners = messageListeners.get(m.getType()).
684 getOrderedListeners();
685 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800686 FloodlightContext bc = null;
687 if (listeners != null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700688 // Check if floodlight context is passed from the calling
689 // function, if so use that floodlight context, otherwise
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800690 // allocate one
691 if (bContext == null) {
692 bc = flcontext_alloc();
693 } else {
694 bc = bContext;
695 }
696 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700697 IFloodlightProviderService.bcStore.put(bc,
698 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800699 eth);
700 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700701 if (inport != -1) {
702 bc.getStorage().put(
703 IFloodlightProviderService.CONTEXT_PI_INPORT,
704 inport);
705 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700706
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700707 // Get the starting time (overall and per-component) of
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800708 // the processing chain for this packet if performance
709 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -0700710
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700711 Command cmd = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800712 for (IOFMessageListener listener : listeners) {
713 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700714 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800715 continue;
716 }
717 }
718
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800719 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -0800720
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800721 if (Command.STOP.equals(cmd)) {
722 break;
723 }
724 }
mininet73e7fb72013-12-03 14:25:53 -0800725
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800726 } else {
727 log.warn("Unhandled OF Message: {} from {}", m, sw);
728 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700729
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800730 if ((bContext == null) && (bc != null)) flcontext_free(bc);
731 }
732 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700733
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800734 // ***************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700735 // IFloodlightProviderService
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800736 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700737
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700738 // FIXME: remove this method
739 @Override
740 public Map<Long,IOFSwitch> getSwitches() {
741 return getMasterSwitches();
742 }
743
744 // FIXME: remove this method
745 public Map<Long, IOFSwitch> getMasterSwitches() {
746 return Collections.unmodifiableMap(activeMasterSwitches);
747 }
748
749
750 @Override
751 public Set<Long> getAllSwitchDpids() {
752 Set<Long> dpids = new HashSet<Long>();
753 dpids.addAll(activeMasterSwitches.keySet());
754 dpids.addAll(activeEqualSwitches.keySet());
755 return dpids;
756 }
757
758 @Override
759 public Set<Long> getAllMasterSwitchDpids() {
760 Set<Long> dpids = new HashSet<Long>();
761 dpids.addAll(activeMasterSwitches.keySet());
762 return dpids;
763 }
764
765 @Override
766 public Set<Long> getAllEqualSwitchDpids() {
767 Set<Long> dpids = new HashSet<Long>();
768 dpids.addAll(activeEqualSwitches.keySet());
769 return dpids;
770 }
771
772 @Override
773 public IOFSwitch getSwitch(long dpid) {
774 IOFSwitch sw = null;
775 if ((sw = activeMasterSwitches.get(dpid)) != null) return sw;
776 if ((sw = activeEqualSwitches.get(dpid)) != null) return sw;
777 return sw;
778 }
779
780 @Override
781 public IOFSwitch getMasterSwitch(long dpid) {
782 return activeMasterSwitches.get(dpid);
783 }
784
785 @Override
786 public IOFSwitch getEqualSwitch(long dpid) {
787 return activeEqualSwitches.get(dpid);
788 }
789
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800790 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700791 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800792 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700793 ListenerDispatcher<OFType, IOFMessageListener> ldd =
794 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800795 if (ldd == null) {
796 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
797 messageListeners.put(type, ldd);
798 }
799 ldd.addListener(type, listener);
800 }
801
802 @Override
803 public synchronized void removeOFMessageListener(OFType type,
804 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700805 ListenerDispatcher<OFType, IOFMessageListener> ldd =
806 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800807 if (ldd != null) {
808 ldd.removeListener(listener);
809 }
810 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700811
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700812 public void removeOFMessageListeners(OFType type) {
813 messageListeners.remove(type);
814 }
815
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800816 private void logListeners() {
817 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700818 ListenerDispatcher<OFType,
819 IOFMessageListener>> entry
820 : messageListeners.entrySet()) {
821
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800822 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -0700823 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800824 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -0700825
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800826 StringBuffer sb = new StringBuffer();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700827 sb.append("OFMessageListeners for ");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800828 sb.append(type);
829 sb.append(": ");
830 for (IOFMessageListener l : ldd.getOrderedListeners()) {
831 sb.append(l.getName());
832 sb.append(",");
833 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700834 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800835 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700836 StringBuffer sl = new StringBuffer();
837 sl.append("SwitchUpdate Listeners: ");
838 for (IOFSwitchListener swlistener : switchListeners) {
839 sl.append(swlistener.getName());
840 sl.append(",");
841 }
842 log.debug(sl.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700843
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800844 }
845
846 @Override
847 public void addOFSwitchListener(IOFSwitchListener listener) {
848 this.switchListeners.add(listener);
849 }
850
851 @Override
852 public void removeOFSwitchListener(IOFSwitchListener listener) {
853 this.switchListeners.remove(listener);
854 }
855
856 @Override
857 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700858 Map<OFType, List<IOFMessageListener>> lers =
859 new HashMap<OFType, List<IOFMessageListener>>();
860 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
861 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800862 lers.put(e.getKey(), e.getValue().getOrderedListeners());
863 }
864 return Collections.unmodifiableMap(lers);
865 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700866
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700867 /*@Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800868 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700869 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
870 "a null switch",
871 explanation = "Failed to process a message because the switch " +
872 " is no longer connected."),
873 @LogMessageDoc(level = "ERROR",
874 message = "Error reinjecting OFMessage on switch {switch}",
875 explanation = "An I/O error occured while attempting to " +
876 "process an OpenFlow message",
877 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800878 })
879 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
880 FloodlightContext bc) {
881 if (sw == null) {
882 log.info("Failed to inject OFMessage {} onto a null switch", msg);
883 return false;
884 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700885
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800886 // FIXME: Do we need to be able to inject messages to switches
887 // where we're the slave controller (i.e. they're connected but
888 // not active)?
889 // FIXME: Don't we need synchronization logic here so we're holding
890 // the listener read lock when we call handleMessage? After some
891 // discussions it sounds like the right thing to do here would be to
892 // inject the message as a netty upstream channel event so it goes
893 // through the normal netty event processing, including being
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700894 // handled
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800895 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700896
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800897 try {
898 // Pass Floodlight context to the handleMessages()
899 handleMessage(sw, msg, bc);
900 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700901 log.error("Error reinjecting OFMessage on switch {}",
902 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800903 return false;
904 }
905 return true;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700906 }*/
907
908
909
910// @Override
911// public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
912// // call the overloaded version with floodlight context set to null
913// return injectOfMessage(sw, msg, null);
914// }
915
916// @Override
917// public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
918// FloodlightContext bc) {
919//
920// List<IOFMessageListener> listeners = null;
921// if (messageListeners.containsKey(m.getType())) {
922// listeners =
923// messageListeners.get(m.getType()).getOrderedListeners();
924// }
925//
926// if (listeners != null) {
927// for (IOFMessageListener listener : listeners) {
928// if (listener instanceof IOFSwitchFilter) {
929// if (!((IOFSwitchFilter) listener).isInterested(sw)) {
930// continue;
931// }
932// }
933// if (Command.STOP.equals(listener.receive(sw, m, bc))) {
934// break;
935// }
936// }
937// }
938// }
939
Jonathan Harta213bce2014-08-11 15:44:07 -0700940
941 /**
942 * Gets an OpenFlow message factory for version 1.0.
943 *
944 * @return an OpenFlow 1.0 message factory
945 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700946 public OFFactory getOFMessageFactory_10() {
947 return factory10;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800948 }
949
Jonathan Harta213bce2014-08-11 15:44:07 -0700950 /**
951 * Gets an OpenFlow message factory for version 1.3.
952 *
953 * @return an OpenFlow 1.3 message factory
954 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700955 public OFFactory getOFMessageFactory_13() {
956 return factory13;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800957 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700958
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800959 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700960 public void publishUpdate(IUpdate update) {
961 try {
962 this.updates.put(update);
963 } catch (InterruptedException e) {
964 log.error("Failure adding update to queue", e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800965 }
966 }
967
968 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700969 public Map<String, String> getControllerNodeIPs() {
970 // We return a copy of the mapping so we can guarantee that
971 // the mapping return is the same as one that will be (or was)
972 // dispatched to IHAListeners
973 HashMap<String, String> retval = new HashMap<String, String>();
974 synchronized (controllerNodeIPsCache) {
975 retval.putAll(controllerNodeIPsCache);
976 }
977 return retval;
978 }
979
980 @Override
981 public long getSystemStartTime() {
982 return (this.systemStartTime);
983 }
984
985 @Override
986 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
987 this.alwaysClearFlowsOnSwAdd = value;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800988 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700989
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800990 @Override
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -0700991 public OnosInstanceId getOnosInstanceId() {
992 return onosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800993 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700994
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700995 /**
996 * FOR TESTING ONLY. Dispatch all updates in the update queue until queue is
997 * empty
998 */
999 void processUpdateQueueForTesting() {
1000 while (!updates.isEmpty()) {
1001 IUpdate update = updates.poll();
1002 if (update != null)
1003 update.dispatch();
1004 }
1005 }
1006
1007
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001008 // **************
1009 // Initialization
1010 // **************
1011
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001012 // XXX S This should probably go away OR it should be edited to handle
1013 // controller roles per switch! Then it could be a way to
1014 // deterministically configure a switch to a MASTER controller instance
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001015 /**
1016 * Sets the initial role based on properties in the config params.
1017 * It looks for two different properties.
1018 * If the "role" property is specified then the value should be
1019 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1020 * controller is set to the specified value. If the "role" property
1021 * is not specified then it looks next for the "role.path" property.
1022 * In this case the value should be the path to a property file in
1023 * the file system that contains a property called "floodlight.role"
1024 * which can be one of the values listed above for the "role" property.
1025 * The idea behind the "role.path" mechanism is that you have some
1026 * separate heartbeat and master controller election algorithm that
1027 * determines the role of the controller. When a role transition happens,
1028 * it updates the current role in the file specified by the "role.path"
1029 * file. Then if floodlight restarts for some reason it can get the
1030 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001031 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001032 * @param configParams The config params for the FloodlightProvider service
1033 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001034 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001035 */
1036 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001037 @LogMessageDoc(message = "Controller role set to {role}",
1038 explanation = "Setting the initial HA role to "),
1039 @LogMessageDoc(level = "ERROR",
1040 message = "Invalid current role value: {role}",
1041 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001042 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001043 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001044 })
1045 protected Role getInitialRole(Map<String, String> configParams) {
1046 Role role = null;
1047 String roleString = configParams.get("role");
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001048 FileInputStream fs = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001049 if (roleString == null) {
1050 String rolePath = configParams.get("rolepath");
1051 if (rolePath != null) {
1052 Properties properties = new Properties();
1053 try {
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001054 fs = new FileInputStream(rolePath);
1055 properties.load(fs);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001056 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001057 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001058 // Don't treat it as an error if the file specified by the
1059 // rolepath property doesn't exist. This lets us enable the
1060 // HA mechanism by just creating/setting the floodlight.role
1061 // property in that file without having to modify the
1062 // floodlight properties.
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001063 } finally {
1064 if (fs != null) {
1065 try {
1066 fs.close();
1067 } catch (IOException e) {
1068 log.error("Exception while closing resource ", e);
1069 }
1070 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001071 }
1072 }
1073 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001074
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001075 if (roleString != null) {
1076 // Canonicalize the string to the form used for the enum constants
1077 roleString = roleString.trim().toUpperCase();
1078 try {
1079 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001080 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001081 log.error("Invalid current role value: {}", roleString);
1082 }
1083 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001084
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001085 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001086
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001087 return role;
1088 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001089
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001090 /**
1091 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001092 *
1093 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001094 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001095 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001096 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001097 @LogMessageDoc(message = "Listening for switch connections on {address}",
1098 explanation = "The controller is ready and listening for new" +
1099 " switch connections"),
1100 @LogMessageDoc(message = "Storage exception in controller " +
1101 "updates loop; terminating process",
1102 explanation = ERROR_DATABASE,
1103 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1104 @LogMessageDoc(level = "ERROR",
1105 message = "Exception in controller updates loop",
1106 explanation = "Failed to dispatch controller event",
1107 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001108 })
1109 public void run() {
1110 if (log.isDebugEnabled()) {
1111 logListeners();
1112 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001113
1114 try {
1115 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001116
1117 bootstrap.setOption("reuseAddr", true);
1118 bootstrap.setOption("child.keepAlive", true);
1119 bootstrap.setOption("child.tcpNoDelay", true);
1120 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1121
Ray Milkey269ffb92014-04-03 14:43:30 -07001122 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001123 new OpenflowPipelineFactory(this, null);
1124 bootstrap.setPipelineFactory(pfact);
1125 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1126 final ChannelGroup cg = new DefaultChannelGroup();
1127 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001128
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001129 log.info("Listening for switch connections on {}", sa);
1130 } catch (Exception e) {
1131 throw new RuntimeException(e);
1132 }
1133
1134 // main loop
1135 while (true) {
1136 try {
1137 IUpdate update = updates.take();
1138 update.dispatch();
1139 } catch (InterruptedException e) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001140 log.error("Received interrupted exception in updates loop;" +
1141 "terminating process");
1142 terminate();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001143 } catch (Exception e) {
1144 log.error("Exception in controller updates loop", e);
1145 }
1146 }
1147 }
1148
1149 private ServerBootstrap createServerBootStrap() {
1150 if (workerThreads == 0) {
1151 return new ServerBootstrap(
1152 new NioServerSocketChannelFactory(
1153 Executors.newCachedThreadPool(),
1154 Executors.newCachedThreadPool()));
1155 } else {
1156 return new ServerBootstrap(
1157 new NioServerSocketChannelFactory(
1158 Executors.newCachedThreadPool(),
1159 Executors.newCachedThreadPool(), workerThreads));
1160 }
1161 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001162
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001163 public void setConfigParams(Map<String, String> configParams) {
1164 String ofPort = configParams.get("openflowport");
1165 if (ofPort != null) {
1166 this.openFlowPort = Integer.parseInt(ofPort);
1167 }
1168 log.debug("OpenFlow port set to {}", this.openFlowPort);
1169 String threads = configParams.get("workerthreads");
1170 if (threads != null) {
1171 this.workerThreads = Integer.parseInt(threads);
1172 }
1173 log.debug("Number of worker threads set to {}", this.workerThreads);
1174 String controllerId = configParams.get("controllerid");
1175 if (controllerId != null) {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001176 this.onosInstanceId = new OnosInstanceId(controllerId);
Ray Milkey269ffb92014-04-03 14:43:30 -07001177 } else {
1178 //Try to get the hostname of the machine and use that for controller ID
1179 try {
1180 String hostname = java.net.InetAddress.getLocalHost().getHostName();
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001181 this.onosInstanceId = new OnosInstanceId(hostname);
Ray Milkey269ffb92014-04-03 14:43:30 -07001182 } catch (UnknownHostException e) {
1183 // Can't get hostname, we'll just use the default
1184 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001185 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001186
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001187 log.debug("ControllerId set to {}", this.onosInstanceId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001188 }
1189
Ray Milkey269ffb92014-04-03 14:43:30 -07001190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001191 /**
1192 * Initialize internal data structures
1193 */
1194 public void init(Map<String, String> configParams) {
1195 // These data structures are initialized here because other
1196 // module's startUp() might be called before ours
1197 this.messageListeners =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001198 new ConcurrentHashMap<OFType, ListenerDispatcher<OFType,
1199 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001200 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001201 this.activeMasterSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1202 this.activeEqualSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1203 this.connectedSwitches = new ConcurrentHashMap<Long, OFChannelHandler>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001204 this.controllerNodeIPsCache = new HashMap<String, String>();
1205 this.updates = new LinkedBlockingQueue<IUpdate>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001206
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001207 setConfigParams(configParams);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001208 this.systemStartTime = System.currentTimeMillis();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001209 this.counters = new Counters();
1210 this.multiCacheLock = new Object();
1211
1212 String option = configParams.get("flushSwitchesOnReconnect");
1213 if (option != null && option.equalsIgnoreCase("true")) {
1214 this.setAlwaysClearFlowsOnSwActivate(true);
1215 log.info("Flush switches on reconnect -- Enabled.");
1216 } else {
1217 this.setAlwaysClearFlowsOnSwActivate(false);
1218 log.info("Flush switches on reconnect -- Disabled");
1219 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001220 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001221
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001222 /**
1223 * Startup all of the controller's components
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001224 * @throws FloodlightModuleException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001225 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001226 @LogMessageDoc(message = "Waiting for storage source",
1227 explanation = "The system database is not yet ready",
1228 recommendation = "If this message persists, this indicates " +
1229 "that the system database has failed to start. " +
1230 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001231 public void startupComponents() throws FloodlightModuleException {
Ray Milkey269ffb92014-04-03 14:43:30 -07001232 try {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001233 registryService.registerController(onosInstanceId.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -07001234 } catch (RegistryException e) {
1235 log.warn("Registry service error: {}", e.getMessage());
1236 }
1237
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001238 // Add our REST API
1239 restApi.addRestletRoutable(new CoreWebRoutable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001240
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001241 // Startup load monitoring
1242 if (overload_drop) {
1243 this.loadmonitor.startMonitoring(
1244 this.threadPool.getScheduledExecutor());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001245 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001246
1247 // register counters and events
1248 try {
1249 this.counters.createCounters(debugCounters);
1250 } catch (CounterException e) {
1251 throw new FloodlightModuleException(e.getMessage());
1252 }
1253 registerControllerDebugEvents();
1254 }
1255
1256 // **************
1257 // debugCounter registrations
1258 // **************
1259
1260 public static class Counters {
1261 public static final String prefix = "controller";
1262 public IDebugCounter setRoleEqual;
1263 public IDebugCounter setSameRole;
1264 public IDebugCounter setRoleMaster;
1265 public IDebugCounter remoteStoreNotification;
1266 public IDebugCounter invalidPortsChanged;
1267 public IDebugCounter invalidSwitchActivatedWhileSlave;
1268 public IDebugCounter invalidStoreEventWhileMaster;
1269 public IDebugCounter switchDisconnectedWhileSlave;
1270 public IDebugCounter switchActivated;
1271 public IDebugCounter errorSameSwitchReactivated; // err
1272 public IDebugCounter switchWithSameDpidActivated; // warn
1273 public IDebugCounter newSwitchActivated; // new switch
1274 public IDebugCounter syncedSwitchActivated;
1275 public IDebugCounter readyForReconcile;
1276 public IDebugCounter newSwitchFromStore;
1277 public IDebugCounter updatedSwitchFromStore;
1278 public IDebugCounter switchDisconnected;
1279 public IDebugCounter syncedSwitchRemoved;
1280 public IDebugCounter unknownSwitchRemovedFromStore;
1281 public IDebugCounter consolidateStoreRunCount;
1282 public IDebugCounter consolidateStoreInconsistencies;
1283 public IDebugCounter storeSyncError;
1284 public IDebugCounter switchesNotReconnectingToNewMaster;
1285 public IDebugCounter switchPortChanged;
1286 public IDebugCounter switchOtherChange;
1287 public IDebugCounter dispatchMessageWhileSlave;
1288 public IDebugCounter dispatchMessage; // does this cnt make sense? more specific?? per type? count stops?
1289 public IDebugCounter controllerNodeIpsChanged;
1290 public IDebugCounter messageReceived;
1291 public IDebugCounter messageInputThrottled;
1292 public IDebugCounter switchDisconnectReadTimeout;
1293 public IDebugCounter switchDisconnectHandshakeTimeout;
1294 public IDebugCounter switchDisconnectIOError;
1295 public IDebugCounter switchDisconnectParseError;
1296 public IDebugCounter switchDisconnectSwitchStateException;
1297 public IDebugCounter rejectedExecutionException;
1298 public IDebugCounter switchDisconnectOtherException;
1299 public IDebugCounter switchConnected;
1300 public IDebugCounter unhandledMessage;
1301 public IDebugCounter packetInWhileSwitchIsSlave;
1302 public IDebugCounter epermErrorWhileSwitchIsMaster;
1303 public IDebugCounter roleNotResentBecauseRolePending;
1304 public IDebugCounter roleRequestSent;
1305 public IDebugCounter roleReplyTimeout;
1306 public IDebugCounter roleReplyReceived; // expected RoleReply received
1307 public IDebugCounter roleReplyErrorUnsupported;
1308 public IDebugCounter switchCounterRegistrationFailed;
1309 public IDebugCounter packetParsingError;
1310
1311 void createCounters(IDebugCounterService debugCounters) throws CounterException {
1312 setRoleEqual =
1313 debugCounters.registerCounter(
1314 prefix, "set-role-equal",
1315 "Controller received a role request with role of "+
1316 "EQUAL which is unusual",
1317 CounterType.ALWAYS_COUNT);
1318 setSameRole =
1319 debugCounters.registerCounter(
1320 prefix, "set-same-role",
1321 "Controller received a role request for the same " +
1322 "role the controller already had",
1323 CounterType.ALWAYS_COUNT,
1324 IDebugCounterService.CTR_MDATA_WARN);
1325
1326 setRoleMaster =
1327 debugCounters.registerCounter(
1328 prefix, "set-role-master",
1329 "Controller received a role request with role of " +
1330 "MASTER. This counter can be at most 1.",
1331 CounterType.ALWAYS_COUNT);
1332
1333 remoteStoreNotification =
1334 debugCounters.registerCounter(
1335 prefix, "remote-store-notification",
1336 "Received a notification from the sync service " +
1337 "indicating that switch information has changed",
1338 CounterType.ALWAYS_COUNT);
1339
1340 invalidPortsChanged =
1341 debugCounters.registerCounter(
1342 prefix, "invalid-ports-changed",
1343 "Received an unexpected ports changed " +
1344 "notification while the controller was in " +
1345 "SLAVE role.",
1346 CounterType.ALWAYS_COUNT,
1347 IDebugCounterService.CTR_MDATA_WARN);
1348
1349 invalidSwitchActivatedWhileSlave =
1350 debugCounters.registerCounter(
1351 prefix, "invalid-switch-activated-while-slave",
1352 "Received an unexpected switchActivated " +
1353 "notification while the controller was in " +
1354 "SLAVE role.",
1355 CounterType.ALWAYS_COUNT,
1356 IDebugCounterService.CTR_MDATA_WARN);
1357
1358 invalidStoreEventWhileMaster =
1359 debugCounters.registerCounter(
1360 prefix, "invalid-store-event-while-master",
1361 "Received an unexpected notification from " +
1362 "the sync store while the controller was in " +
1363 "MASTER role.",
1364 CounterType.ALWAYS_COUNT,
1365 IDebugCounterService.CTR_MDATA_WARN);
1366
1367 switchDisconnectedWhileSlave =
1368 debugCounters.registerCounter(
1369 prefix, "switch-disconnected-while-slave",
1370 "A switch disconnected and the controller was " +
1371 "in SLAVE role.",
1372 CounterType.ALWAYS_COUNT,
1373 IDebugCounterService.CTR_MDATA_WARN);
1374
1375 switchActivated =
1376 debugCounters.registerCounter(
1377 prefix, "switch-activated",
1378 "A switch connected to this controller is now " +
1379 "in MASTER role",
1380 CounterType.ALWAYS_COUNT);
1381
1382 errorSameSwitchReactivated = // err
1383 debugCounters.registerCounter(
1384 prefix, "error-same-switch-reactivated",
1385 "A switch that was already in active state " +
1386 "was activated again. This indicates a " +
1387 "controller defect",
1388 CounterType.ALWAYS_COUNT,
1389 IDebugCounterService.CTR_MDATA_ERROR);
1390
1391 switchWithSameDpidActivated = // warn
1392 debugCounters.registerCounter(
1393 prefix, "switch-with-same-dpid-activated",
1394 "A switch with the same DPID as another switch " +
1395 "connected to the controller. This can be " +
1396 "caused by multiple switches configured with " +
1397 "the same DPID or by a switch reconnecting very " +
1398 "quickly.",
1399 CounterType.COUNT_ON_DEMAND,
1400 IDebugCounterService.CTR_MDATA_WARN);
1401
1402 newSwitchActivated = // new switch
1403 debugCounters.registerCounter(
1404 prefix, "new-switch-activated",
1405 "A new switch has completed the handshake as " +
1406 "MASTER. The switch was not known to any other " +
1407 "controller in the cluster",
1408 CounterType.ALWAYS_COUNT);
1409 syncedSwitchActivated =
1410 debugCounters.registerCounter(
1411 prefix, "synced-switch-activated",
1412 "A switch has completed the handshake as " +
1413 "MASTER. The switch was known to another " +
1414 "controller in the cluster",
1415 CounterType.ALWAYS_COUNT);
1416
1417 readyForReconcile =
1418 debugCounters.registerCounter(
1419 prefix, "ready-for-reconcile",
1420 "Controller is ready for flow reconciliation " +
1421 "after Slave to Master transition. Either all " +
1422 "previously known switches are now active " +
1423 "or they have timed out and have been removed." +
1424 "This counter will be 0 or 1.",
1425 CounterType.ALWAYS_COUNT);
1426
1427 newSwitchFromStore =
1428 debugCounters.registerCounter(
1429 prefix, "new-switch-from-store",
1430 "A new switch has connected to another " +
1431 "another controller in the cluster. This " +
1432 "controller instance has received a sync store " +
1433 "notification for it.",
1434 CounterType.ALWAYS_COUNT);
1435
1436 updatedSwitchFromStore =
1437 debugCounters.registerCounter(
1438 prefix, "updated-switch-from-store",
1439 "Information about a switch connected to " +
1440 "another controller instance was updated in " +
1441 "the sync store. This controller instance has " +
1442 "received a notification for it",
1443 CounterType.ALWAYS_COUNT);
1444
1445 switchDisconnected =
1446 debugCounters.registerCounter(
1447 prefix, "switch-disconnected",
1448 "FIXME: switch has disconnected",
1449 CounterType.ALWAYS_COUNT);
1450
1451 syncedSwitchRemoved =
1452 debugCounters.registerCounter(
1453 prefix, "synced-switch-removed",
1454 "A switch connected to another controller " +
1455 "instance has disconnected from the controller " +
1456 "cluster. This controller instance has " +
1457 "received a notification for it",
1458 CounterType.ALWAYS_COUNT);
1459
1460 unknownSwitchRemovedFromStore =
1461 debugCounters.registerCounter(
1462 prefix, "unknown-switch-removed-from-store",
1463 "This controller instances has received a sync " +
1464 "store notification that a switch has " +
1465 "disconnected but this controller instance " +
1466 "did not have the any information about the " +
1467 "switch", // might be less than warning
1468 CounterType.ALWAYS_COUNT,
1469 IDebugCounterService.CTR_MDATA_WARN);
1470
1471 consolidateStoreRunCount =
1472 debugCounters.registerCounter(
1473 prefix, "consolidate-store-run-count",
1474 "This controller has transitioned from SLAVE " +
1475 "to MASTER and waited for switches to reconnect. " +
1476 "The controller has finished waiting and has " +
1477 "reconciled switch entries in the sync store " +
1478 "with live state",
1479 CounterType.ALWAYS_COUNT);
1480
1481 consolidateStoreInconsistencies =
1482 debugCounters.registerCounter(
1483 prefix, "consolidate-store-inconsistencies",
1484 "During switch sync store consolidation: " +
1485 "Number of switches that were in the store " +
1486 "but not otherwise known plus number of " +
1487 "switches that were in the store previously " +
1488 "but are now missing plus number of " +
1489 "connected switches that were absent from " +
1490 "the store although this controller has " +
1491 "written them. A non-zero count " +
1492 "indicates a brief split-brain dual MASTER " +
1493 "situation during fail-over",
1494 CounterType.ALWAYS_COUNT);
1495
1496 storeSyncError =
1497 debugCounters.registerCounter(
1498 prefix, "store-sync-error",
1499 "Number of times a sync store operation failed " +
1500 "due to a store sync exception or an entry in " +
1501 "in the store had invalid data.",
1502 CounterType.ALWAYS_COUNT,
1503 IDebugCounterService.CTR_MDATA_ERROR);
1504
1505 switchesNotReconnectingToNewMaster =
1506 debugCounters.registerCounter(
1507 prefix, "switches-not-reconnecting-to-new-master",
1508 "Switches that were connected to another " +
1509 "controller instance in the cluster but that " +
1510 "did not reconnect to this controller after it " +
1511 "transitioned to MASTER", // might be less than warning
1512 CounterType.ALWAYS_COUNT);
1513
1514 switchPortChanged =
1515 debugCounters.registerCounter(
1516 prefix, "switch-port-changed",
1517 "Number of times switch ports have changed",
1518 CounterType.ALWAYS_COUNT);
1519 switchOtherChange =
1520 debugCounters.registerCounter(
1521 prefix, "switch-other-change",
1522 "Number of times other information of a switch " +
1523 "has changed.",
1524 CounterType.ALWAYS_COUNT);
1525
1526 dispatchMessageWhileSlave =
1527 debugCounters.registerCounter(
1528 prefix, "dispatch-message-while-slave",
1529 "Number of times an OF message was received " +
1530 "and supposed to be dispatched but the " +
1531 "controller was in SLAVE role and the message " +
1532 "was not dispatched",
1533 CounterType.ALWAYS_COUNT);
1534
1535 dispatchMessage = // does this cnt make sense? more specific?? per type? count stops?
1536 debugCounters.registerCounter(
1537 prefix, "dispatch-message",
1538 "Number of times an OF message was dispatched " +
1539 "to registered modules",
1540 CounterType.ALWAYS_COUNT);
1541
1542 controllerNodeIpsChanged =
1543 debugCounters.registerCounter(
1544 prefix, "controller-nodes-ips-changed",
1545 "IP addresses of controller nodes have changed",
1546 CounterType.ALWAYS_COUNT);
1547
1548 //------------------------
1549 // channel handler counters. Factor them out ??
1550 messageReceived =
1551 debugCounters.registerCounter(
1552 prefix, "message-received",
1553 "Number of OpenFlow messages received. Some of " +
1554 "these might be throttled",
1555 CounterType.ALWAYS_COUNT);
1556 messageInputThrottled =
1557 debugCounters.registerCounter(
1558 prefix, "message-input-throttled",
1559 "Number of OpenFlow messages that were " +
1560 "throttled due to high load from the sender",
1561 CounterType.ALWAYS_COUNT,
1562 IDebugCounterService.CTR_MDATA_WARN);
1563 // TODO: more counters in messageReceived ??
1564
1565 switchDisconnectReadTimeout =
1566 debugCounters.registerCounter(
1567 prefix, "switch-disconnect-read-timeout",
1568 "Number of times a switch was disconnected due " +
1569 "due the switch failing to send OpenFlow " +
1570 "messages or responding to OpenFlow ECHOs",
1571 CounterType.ALWAYS_COUNT,
1572 IDebugCounterService.CTR_MDATA_ERROR);
1573 switchDisconnectHandshakeTimeout =
1574 debugCounters.registerCounter(
1575 prefix, "switch-disconnect-handshake-timeout",
1576 "Number of times a switch was disconnected " +
1577 "because it failed to complete the handshake " +
1578 "in time.",
1579 CounterType.ALWAYS_COUNT,
1580 IDebugCounterService.CTR_MDATA_ERROR);
1581 switchDisconnectIOError =
1582 debugCounters.registerCounter(
1583 prefix, "switch-disconnect-io-error",
1584 "Number of times a switch was disconnected " +
1585 "due to IO errors on the switch connection.",
1586 CounterType.ALWAYS_COUNT,
1587 IDebugCounterService.CTR_MDATA_ERROR);
1588 switchDisconnectParseError =
1589 debugCounters.registerCounter(
1590 prefix, "switch-disconnect-parse-error",
1591 "Number of times a switch was disconnected " +
1592 "because it sent an invalid packet that could " +
1593 "not be parsed",
1594 CounterType.ALWAYS_COUNT,
1595 IDebugCounterService.CTR_MDATA_ERROR);
1596
1597 switchDisconnectSwitchStateException =
1598 debugCounters.registerCounter(
1599 prefix, "switch-disconnect-switch-state-exception",
1600 "Number of times a switch was disconnected " +
1601 "because it sent messages that were invalid " +
1602 "given the switch connection's state.",
1603 CounterType.ALWAYS_COUNT,
1604 IDebugCounterService.CTR_MDATA_ERROR);
1605 rejectedExecutionException =
1606 debugCounters.registerCounter(
1607 prefix, "rejected-execution-exception",
1608 "TODO",
1609 CounterType.ALWAYS_COUNT,
1610 IDebugCounterService.CTR_MDATA_ERROR);
1611
1612 switchDisconnectOtherException =
1613 debugCounters.registerCounter(
1614 prefix, "switch-disconnect-other-exception",
1615 "Number of times a switch was disconnected " +
1616 "due to an exceptional situation not covered " +
1617 "by other counters",
1618 CounterType.ALWAYS_COUNT,
1619 IDebugCounterService.CTR_MDATA_ERROR);
1620
1621 switchConnected =
1622 debugCounters.registerCounter(
1623 prefix, "switch-connected",
1624 "Number of times a new switch connection was " +
1625 "established",
1626 CounterType.ALWAYS_COUNT);
1627
1628 unhandledMessage =
1629 debugCounters.registerCounter(
1630 prefix, "unhandled-message",
1631 "Number of times an OpenFlow message was " +
1632 "received that the controller ignored because " +
1633 "it was inapproriate given the switch " +
1634 "connection's state.",
1635 CounterType.ALWAYS_COUNT,
1636 IDebugCounterService.CTR_MDATA_WARN);
1637 // might be less than warning
1638
1639 packetInWhileSwitchIsSlave =
1640 debugCounters.registerCounter(
1641 prefix, "packet-in-while-switch-is-slave",
1642 "Number of times a packet in was received " +
1643 "from a switch that was in SLAVE role. " +
1644 "Possibly inidicates inconsistent roles.",
1645 CounterType.ALWAYS_COUNT);
1646 epermErrorWhileSwitchIsMaster =
1647 debugCounters.registerCounter(
1648 prefix, "eperm-error-while-switch-is-master",
1649 "Number of times a permission error was " +
1650 "received while the switch was in MASTER role. " +
1651 "Possibly inidicates inconsistent roles.",
1652 CounterType.ALWAYS_COUNT,
1653 IDebugCounterService.CTR_MDATA_WARN);
1654
1655 roleNotResentBecauseRolePending =
1656 debugCounters.registerCounter(
1657 prefix, "role-not-resent-because-role-pending",
1658 "The controller tried to reestablish a role " +
1659 "with a switch but did not do so because a " +
1660 "previous role request was still pending",
1661 CounterType.ALWAYS_COUNT);
1662 roleRequestSent =
1663 debugCounters.registerCounter(
1664 prefix, "role-request-sent",
1665 "Number of times the controller sent a role " +
1666 "request to a switch.",
1667 CounterType.ALWAYS_COUNT);
1668 roleReplyTimeout =
1669 debugCounters.registerCounter(
1670 prefix, "role-reply-timeout",
1671 "Number of times a role request message did not " +
1672 "receive the expected reply from a switch",
1673 CounterType.ALWAYS_COUNT,
1674 IDebugCounterService.CTR_MDATA_WARN);
1675
1676 roleReplyReceived = // expected RoleReply received
1677 debugCounters.registerCounter(
1678 prefix, "role-reply-received",
1679 "Number of times the controller received the " +
1680 "expected role reply message from a switch",
1681 CounterType.ALWAYS_COUNT);
1682
1683 roleReplyErrorUnsupported =
1684 debugCounters.registerCounter(
1685 prefix, "role-reply-error-unsupported",
1686 "Number of times the controller received an " +
1687 "error from a switch in response to a role " +
1688 "request indicating that the switch does not " +
1689 "support roles.",
1690 CounterType.ALWAYS_COUNT);
1691
1692 switchCounterRegistrationFailed =
1693 debugCounters.registerCounter(prefix,
1694 "switch-counter-registration-failed",
1695 "Number of times the controller failed to " +
1696 "register per-switch debug counters",
1697 CounterType.ALWAYS_COUNT,
1698 IDebugCounterService.CTR_MDATA_WARN);
1699
1700 packetParsingError =
1701 debugCounters.registerCounter(prefix,
1702 "packet-parsing-error",
1703 "Number of times the packet parsing " +
1704 "encountered an error",
1705 CounterType.ALWAYS_COUNT,
1706 IDebugCounterService.CTR_MDATA_ERROR);
1707 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001708 }
1709
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001710 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001711 public Counters getCounters() {
1712 return this.counters;
1713 }
1714
1715 // **************
1716 // debugEvent registrations
1717 // **************
1718
1719 private void registerControllerDebugEvents() throws FloodlightModuleException {
1720 if (debugEvents == null) {
1721 debugEvents = new NullDebugEvent();
1722 }
1723 try {
1724 evSwitch = debugEvents.registerEvent(
1725 Counters.prefix, "switchevent",
1726 "Switch connected, disconnected or port changed",
1727 EventType.ALWAYS_LOG, SwitchEvent.class, 100);
1728 } catch (MaxEventsRegistered e) {
1729 throw new FloodlightModuleException("Max events registered", e);
1730 }
1731 }
1732
1733 public class SwitchEvent {
1734 @EventColumn(name = "dpid", description = EventFieldType.DPID)
1735 long dpid;
1736
1737 @EventColumn(name = "reason", description = EventFieldType.STRING)
1738 String reason;
1739
1740 public SwitchEvent(long dpid, String reason) {
1741 this.dpid = dpid;
1742 this.reason = reason;
1743 }
1744 }
1745
1746 // **************
1747 // Utility methods
1748 // **************
1749
1750 @Override
1751 public void setAlwaysClearFlowsOnSwActivate(boolean value) {
1752 //this.alwaysClearFlowsOnSwActivate = value;
1753 // XXX S need to be a little more careful about this
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001754 }
1755
1756 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001757 public Map<String, Long> getMemory() {
1758 Map<String, Long> m = new HashMap<String, Long>();
1759 Runtime runtime = Runtime.getRuntime();
1760 m.put("total", runtime.totalMemory());
1761 m.put("free", runtime.freeMemory());
1762 return m;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001763 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001764
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001765 @Override
1766 public Long getUptime() {
1767 RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
1768 return rb.getUptime();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001769 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001770
1771 /**
1772 * Forward to the driver-manager to get an IOFSwitch instance.
1773 * @param desc
1774 * @return
1775 */
1776 protected IOFSwitch getOFSwitchInstance(OFDescStatsReply desc, OFVersion ofv) {
1777 return DriverManager.getOFSwitchImpl(desc, ofv);
1778 }
1779
1780 protected IThreadPoolService getThreadPoolService() {
1781 return this.threadPool;
1782 }
1783
1784 /**
1785 * Part of the controller updates framework (see 'run()' method)
1786 * Use this method to add an IUpdate. A thread-pool will serve the update
1787 * by dispatching it to all listeners for that update.
1788 * @param update
1789 */
1790 @LogMessageDoc(level="WARN",
1791 message="Failure adding update {} to queue",
1792 explanation="The controller tried to add an internal notification" +
1793 " to its message queue but the add failed.",
1794 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
1795 private void addUpdateToQueue(IUpdate update) {
1796 try {
1797 this.updates.put(update);
1798 } catch (InterruptedException e) {
1799 // This should never happen
1800 log.error("Failure adding update {} to queue.", update);
1801 }
1802 }
1803
1804 void flushAll() {
1805 // Flush all flow-mods/packet-out/stats generated from this "train"
1806 OFSwitchImplBase.flush_all();
1807 debugCounters.flushCounters();
1808 debugEvents.flushEvents();
1809 }
1810
1811 /**
1812 * flcontext_free - Free the context to the current thread
1813 *
1814 * @param flcontext
1815 */
1816 protected void flcontext_free(FloodlightContext flcontext) {
1817 flcontext.getStorage().clear();
1818 flcontext_cache.get().push(flcontext);
1819 }
1820
1821 @LogMessageDoc(message = "Calling System.exit",
1822 explanation = "The controller is terminating")
1823 private synchronized void terminate() {
1824 log.info("Calling System.exit");
1825 System.exit(1);
1826 }
1827
1828
1829 // ***************
1830 // Floodlight context related
1831 // ***************
1832
1833 /**
1834 * flcontext_cache - Keep a thread local stack of contexts
1835 */
1836 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
1837 new ThreadLocal<Stack<FloodlightContext>>() {
1838 @Override
1839 protected Stack<FloodlightContext> initialValue() {
1840 return new Stack<FloodlightContext>();
1841 }
1842 };
1843
1844 /**
1845 * flcontext_alloc - pop a context off the stack, if required create a new one
1846 *
1847 * @return FloodlightContext
1848 */
1849 protected static FloodlightContext flcontext_alloc() {
1850 FloodlightContext flcontext = null;
1851
1852 if (flcontext_cache.get().empty()) {
1853 flcontext = new FloodlightContext();
1854 } else {
1855 flcontext = flcontext_cache.get().pop();
1856 }
1857
1858 return flcontext;
1859 }
1860
1861
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001862}