blob: e80311bcbb85789fe231928e6045842b31383c30 [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;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070038import java.util.concurrent.CopyOnWriteArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080039import java.util.concurrent.CopyOnWriteArraySet;
40import java.util.concurrent.Executors;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041import java.util.concurrent.LinkedBlockingQueue;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080042
43import net.floodlightcontroller.core.FloodlightContext;
44import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080045import net.floodlightcontroller.core.IListener.Command;
Jonathan Hartd10008d2013-02-23 17:04:08 -080046import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080047import net.floodlightcontroller.core.IOFSwitch;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070048import net.floodlightcontroller.core.IOFSwitch.PortChangeType;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080049import net.floodlightcontroller.core.IOFSwitchFilter;
50import net.floodlightcontroller.core.IOFSwitchListener;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070051import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import net.floodlightcontroller.core.annotations.LogMessageDoc;
53import net.floodlightcontroller.core.annotations.LogMessageDocs;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070054import net.floodlightcontroller.core.internal.OFChannelHandler.RoleRecvStatus;
55import net.floodlightcontroller.core.module.FloodlightModuleException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080056import net.floodlightcontroller.core.util.ListenerDispatcher;
57import net.floodlightcontroller.core.web.CoreWebRoutable;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070058import net.floodlightcontroller.debugcounter.IDebugCounter;
59import net.floodlightcontroller.debugcounter.IDebugCounterService;
60import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
61import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
62import net.floodlightcontroller.debugevent.IDebugEventService;
63import net.floodlightcontroller.debugevent.IDebugEventService.EventColumn;
64import net.floodlightcontroller.debugevent.IDebugEventService.EventFieldType;
65import net.floodlightcontroller.debugevent.IDebugEventService.EventType;
66import net.floodlightcontroller.debugevent.IDebugEventService.MaxEventsRegistered;
67import net.floodlightcontroller.debugevent.IEventUpdater;
68import net.floodlightcontroller.debugevent.NullDebugEvent;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080069import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080070import net.floodlightcontroller.threadpool.IThreadPoolService;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070071import net.floodlightcontroller.util.LoadMonitor;
72import net.onrc.onos.core.drivermanager.DriverManager;
Jonathan Hart23701d12014-04-03 10:45:48 -070073import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070074import net.onrc.onos.core.packet.Ethernet;
75import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070076import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070077import net.onrc.onos.core.registry.RegistryException;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070078import net.onrc.onos.core.util.Dpid;
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -070079import net.onrc.onos.core.util.OnosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080080
81import org.jboss.netty.bootstrap.ServerBootstrap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080082import org.jboss.netty.channel.ChannelPipelineFactory;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080083import org.jboss.netty.channel.group.ChannelGroup;
84import org.jboss.netty.channel.group.DefaultChannelGroup;
85import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070086import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
87import org.projectfloodlight.openflow.protocol.OFFactories;
88import org.projectfloodlight.openflow.protocol.OFFactory;
89import org.projectfloodlight.openflow.protocol.OFMessage;
90import org.projectfloodlight.openflow.protocol.OFPacketIn;
91import org.projectfloodlight.openflow.protocol.OFPortDesc;
92import org.projectfloodlight.openflow.protocol.OFType;
93import org.projectfloodlight.openflow.protocol.OFVersion;
94import org.projectfloodlight.openflow.protocol.match.MatchField;
95import org.projectfloodlight.openflow.protocol.match.MatchFields;
96import org.projectfloodlight.openflow.types.EthType;
97import org.projectfloodlight.openflow.types.OFPort;
98import org.projectfloodlight.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080099import org.slf4j.Logger;
100import org.slf4j.LoggerFactory;
101
102
103/**
104 * The main controller class. Handles all setup and network listeners
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700105 * - Distributed ownership control of switch through IControllerRegistryService
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800106 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800107public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700108
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700109 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700110 static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800111 "The controller could not communicate with the system database.";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700112 protected static OFFactory factory13 = OFFactories.getFactory(OFVersion.OF_13);
113 protected static OFFactory factory10 = OFFactories.getFactory(OFVersion.OF_10);
Ray Milkey269ffb92014-04-03 14:43:30 -0700114
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700115 // connectedSwitches cache contains all connected switch's channelHandlers
116 // including ones where this controller is a master/equal/slave controller
117 // as well as ones that have not been activated yet
118 protected ConcurrentHashMap<Long, OFChannelHandler> connectedSwitches;
119 // These caches contains only those switches that are active
120 protected ConcurrentHashMap<Long, IOFSwitch> activeMasterSwitches;
121 protected ConcurrentHashMap<Long, IOFSwitch> activeEqualSwitches;
122 // lock to synchronize on, when manipulating multiple caches above
123 private Object multiCacheLock;
Ray Milkey269ffb92014-04-03 14:43:30 -0700124
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700125 // The controllerNodeIPsCache maps Controller IDs to their IP address.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800126 // It's only used by handleControllerNodeIPsChanged
127 protected HashMap<String, String> controllerNodeIPsCache;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800128 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700129
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700130 protected ConcurrentMap<OFType,
131 ListenerDispatcher<OFType, IOFMessageListener>> messageListeners;
132 protected Set<IOFSwitchListener> switchListeners;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700133
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800134 // Module dependencies
135 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800136 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800137 protected IControllerRegistryService registryService;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700138 protected IDebugCounterService debugCounters;
139 protected IDebugEventService debugEvents;
Ray Milkey269ffb92014-04-03 14:43:30 -0700140
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800141 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700142
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800143 // Configuration options
144 protected int openFlowPort = 6633;
145 protected int workerThreads = 0;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700146
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800147 // The id for this controller node. Should be unique for each controller
148 // node in a controller cluster.
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -0700149 private OnosInstanceId onosInstanceId = new OnosInstanceId("localhost");
Ray Milkey269ffb92014-04-03 14:43:30 -0700150
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700151 // defined counters
152 private Counters counters;
153 // Event IDs for debug events
154 protected IEventUpdater<SwitchEvent> evSwitch;
155
156 // Load monitor for overload protection
157 protected final boolean overload_drop =
158 Boolean.parseBoolean(System.getProperty("overload_drop", "false"));
159 protected final LoadMonitor loadmonitor = new LoadMonitor(log);
Ray Milkey269ffb92014-04-03 14:43:30 -0700160
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800161 // Start time of the controller
162 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700163
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800164 // Flag to always flush flow table on switch reconnect (HA or otherwise)
165 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700166
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800167 // Perf. related configuration
168 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
169 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700170 protected static final boolean ALWAYS_DECODE_ETH = true;
171
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700172
173
174 // ******************************
175 // Switch Management and Updates
176 // ******************************
177
178 /**
179 * Switch updates are sent to all IOFSwitchListeners. A switch that is connected
180 * to this controller instance, but not activated, is not available for updates.
181 *
182 * In ONOS, each controller instance can simultaneously serve in a MASTER role
183 * for some connected switches, and in a EQUAL role for other connected switches.
184 * The EQUAL role can be treated as a SLAVE role, by ensuring that the
185 * controller instance never sends packets or commands out to the switch.
186 * Activated switches, either with Controller Role MASTER or EQUAL are announced
187 * as updates. We also support announcements of controller role transitions
188 * from MASTER --> EQUAL, and EQUAL --> MASTER, for an individual switch.
189 *
190 * Disconnection of only activated switches are announced. Finally, changes
191 * to switch ports are announced with a portChangeType (see @IOFSwitch)
192 *
193 * @author saurav
194 */
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800195 public enum SwitchUpdateType {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700196 /** switch activated with this controller's role as MASTER */
197 ACTIVATED_MASTER,
198 /** switch activated with this controller's role as EQUAL.
199 * listener can treat this controller's role as SLAVE by not
200 * sending packets or commands to the switch */
201 ACTIVATED_EQUAL,
202 /** this controller's role for this switch changed from Master to Equal */
203 MASTER_TO_EQUAL,
204 /** this controller's role for this switch changed form Equal to Master */
205 EQUAL_TO_MASTER,
206 /** A previously activated switch disconnected */
207 DISCONNECTED,
208 /** Port changed on a previously activated switch */
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700209 PORTCHANGED,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800210 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700211
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800212 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700213 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700214 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800215 */
216 protected class SwitchUpdate implements IUpdate {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700217 public long getSwId() {
218 return swId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800219 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700220
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700221 public SwitchUpdateType getSwitchUpdateType() {
222 return switchUpdateType;
223 }
224
225 public PortChangeType getPortChangeType() {
226 return changeType;
227 }
228
229 private final long swId;
230 private final SwitchUpdateType switchUpdateType;
231 private final OFPortDesc port;
232 private final PortChangeType changeType;
233
234 public SwitchUpdate(long swId, SwitchUpdateType switchUpdateType) {
235 this(swId, switchUpdateType, null, null);
236 }
237 public SwitchUpdate(long swId,
238 SwitchUpdateType switchUpdateType,
239 OFPortDesc port,
240 PortChangeType changeType) {
241 if (switchUpdateType == SwitchUpdateType.PORTCHANGED) {
242 if (port == null) {
243 throw new NullPointerException("Port must not be null " +
244 "for PORTCHANGED updates");
245 }
246 if (changeType == null) {
247 throw new NullPointerException("ChangeType must not be " +
248 "null for PORTCHANGED updates");
249 }
250 } else {
251 if (port != null || changeType != null) {
252 throw new IllegalArgumentException("port and changeType " +
253 "must be null for " + switchUpdateType +
254 " updates");
255 }
256 }
257 this.swId = swId;
258 this.switchUpdateType = switchUpdateType;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700259 this.port = port;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700260 this.changeType = changeType;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700261 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700262
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700263 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800264 public void dispatch() {
265 if (log.isTraceEnabled()) {
266 log.trace("Dispatching switch update {} {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700267 HexString.toHexString(swId), switchUpdateType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800268 }
269 if (switchListeners != null) {
270 for (IOFSwitchListener listener : switchListeners) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700271 switch(switchUpdateType) {
272 case ACTIVATED_MASTER:
273 // don't count here. We have more specific
274 // counters before the update is created
275 listener.switchActivatedMaster(swId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800276 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700277 case ACTIVATED_EQUAL:
278 // don't count here. We have more specific
279 // counters before the update is created
280 listener.switchActivatedEqual(swId);
281 break;
282 case MASTER_TO_EQUAL:
283 listener.switchMasterToEqual(swId);
284 break;
285 case EQUAL_TO_MASTER:
286 listener.switchEqualToMaster(swId);
287 break;
288 case DISCONNECTED:
289 // don't count here. We have more specific
290 // counters before the update is created
291 listener.switchDisconnected(swId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800292 break;
293 case PORTCHANGED:
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700294 counters.switchPortChanged.updateCounterWithFlush();
295 listener.switchPortChanged(swId, port, changeType);
Ray Milkey269ffb92014-04-03 14:43:30 -0700296 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800297 }
298 }
299 }
300 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700301
302
303 }
304
305 protected boolean addConnectedSwitch(long dpid, OFChannelHandler h) {
306 if (connectedSwitches.get(dpid) != null) {
307 log.error("Trying to add connectedSwitch but found a previous "
308 + "value for dpid: {}", dpid);
309 return false;
310 } else {
311 connectedSwitches.put(dpid, h);
312 return true;
313 }
314 }
315
316 /**
317 * Switch Events
318 */
319 @Override
320 public void addSwitchEvent(long dpid, String reason, boolean flushNow) {
321 if (flushNow)
322 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, reason));
323 else
324 evSwitch.updateEventNoFlush(new SwitchEvent(dpid, reason));
325 }
326
327 private boolean validActivation(long dpid) {
328 if (connectedSwitches.get(dpid) == null) {
329 log.error("Trying to activate switch but is not in "
330 + "connected switches: dpid {}. Aborting ..",
331 HexString.toHexString(dpid));
332 return false;
333 }
334 if (activeMasterSwitches.get(dpid) != null ||
335 activeEqualSwitches.get(dpid) != null) {
336 log.error("Trying to activate switch but it is already "
337 + "activated: dpid {}. Found in activeMaster: {} "
338 + "Found in activeEqual: {}. Aborting ..", new Object[] {
339 HexString.toHexString(dpid),
340 (activeMasterSwitches.get(dpid) == null)? 'Y': 'N',
341 (activeEqualSwitches.get(dpid) == null)? 'Y': 'N'});
342 counters.switchWithSameDpidActivated.updateCounterWithFlush();
343 return false;
344 }
345 return true;
346 }
347
348 /**
349 * Called when a switch is activated, with this controller's role as MASTER
350 */
351 protected boolean addActivatedMasterSwitch(long dpid, IOFSwitch sw) {
352 synchronized(multiCacheLock) {
353 if (!validActivation(dpid)) return false;
354 activeMasterSwitches.put(dpid, sw);
355 }
356 // XXX Workaround to prevent race condition where a link is detected
357 // and attempted to be written to the database before the port is in
358 // the database. We now suppress link discovery on ports until we're
359 // sure they're in the database.
360 for (OFPortDesc port : sw.getPorts()) {
361 linkDiscovery.disableDiscoveryOnPort(sw.getId(),
362 port.getPortNo().getShortPortNumber());
363 }
364 //update counters and events
365 counters.switchActivated.updateCounterWithFlush();
366 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "activeMaster"));
367 addUpdateToQueue(new SwitchUpdate(dpid,
368 SwitchUpdateType.ACTIVATED_MASTER));
369 return true;
370 }
371
372 /**
373 * Called when a switch is activated, with this controller's role as EQUAL
374 */
375 protected boolean addActivatedEqualSwitch(long dpid, IOFSwitch sw) {
376 synchronized(multiCacheLock) {
377 if (!validActivation(dpid)) return false;
378 activeEqualSwitches.put(dpid, sw);
379 }
380 //update counters and events
381 counters.switchActivated.updateCounterWithFlush();
382 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "activeEqual"));
383 addUpdateToQueue(new SwitchUpdate(dpid,
384 SwitchUpdateType.ACTIVATED_EQUAL));
385 return true;
386 }
387
388 /**
389 * Called when this controller's role for a switch transitions from equal
390 * to master. For 1.0 switches, we internally refer to the role 'slave' as
391 * 'equal' - so this transition is equivalent to 'addActivatedMasterSwitch'.
392 */
393 protected void transitionToMasterSwitch(long dpid) {
394 synchronized(multiCacheLock) {
395 IOFSwitch sw = activeEqualSwitches.remove(dpid);
396 if (sw == null) {
397 log.error("Transition to master called on sw {}, but switch "
398 + "was not found in controller-cache", dpid);
399 return;
400 }
401 activeMasterSwitches.put(dpid, sw);
402 }
403 addUpdateToQueue(new SwitchUpdate(dpid,
404 SwitchUpdateType.EQUAL_TO_MASTER));
405 }
406
407
408 /**
409 * Called when this controller's role for a switch transitions to equal.
410 * For 1.0 switches, we internally refer to the role 'slave' as
411 * 'equal'.
412 */
413 protected void transitionToEqualSwitch(long dpid) {
414 synchronized(multiCacheLock) {
415 IOFSwitch sw = activeMasterSwitches.remove(dpid);
416 if (sw == null) {
417 log.error("Transition to equal called on sw {}, but switch "
418 + "was not found in controller-cache", dpid);
419 return;
420 }
421 activeEqualSwitches.put(dpid, sw);
422 }
423 addUpdateToQueue(new SwitchUpdate(dpid,
424 SwitchUpdateType.MASTER_TO_EQUAL));
425 }
426
427 /**
428 * Clear all state in controller switch maps for a switch that has
429 * disconnected from the local controller. Also release control for
430 * that switch from the global repository. Notify switch listeners.
431 */
432 protected void removeConnectedSwitch(long dpid) {
433 releaseRegistryControl(dpid);
434 connectedSwitches.remove(dpid);
435 IOFSwitch sw = activeMasterSwitches.remove(dpid);
436 if (sw == null) sw = activeEqualSwitches.remove(dpid);
437 if (sw != null) {
438 sw.cancelAllStatisticsReplies();
439 sw.setConnected(false); // do we need this?
440 }
441 evSwitch.updateEventWithFlush(new SwitchEvent(dpid, "disconnected"));
442 counters.switchDisconnected.updateCounterWithFlush();
443 addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.DISCONNECTED));
444 }
445
446 /**
447 * Indicates that ports on the given switch have changed. Enqueue a
448 * switch update.
449 * @param sw
450 */
451 protected void notifyPortChanged(long dpid, OFPortDesc port,
452 PortChangeType changeType) {
453 if (port == null || changeType == null) {
454 String msg = String.format("Switch port or changetType must not "
455 + "be null in port change notification");
456 throw new NullPointerException(msg);
457 }
458 if (connectedSwitches.get(dpid) == null || getSwitch(dpid) == null) {
459 log.warn("Port change update on switch {} not connected or activated "
460 + "... Aborting.", HexString.toHexString(dpid));
461 return;
462 }
463
464 if (changeType == PortChangeType.ADD) {
465 // XXX Workaround to prevent race condition where a link is detected
466 // and attempted to be written to the database before the port is in
467 // the database. We now suppress link discovery on ports until we're
468 // sure they're in the database.
469 linkDiscovery.disableDiscoveryOnPort(dpid, port.getPortNo().getShortPortNumber());
470 }
471
472 SwitchUpdate update = new SwitchUpdate(dpid, SwitchUpdateType.PORTCHANGED,
473 port, changeType);
474 addUpdateToQueue(update);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800475 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700476
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800477 // ***************
478 // Getters/Setters
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700479 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700480
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800481 public void setRestApiService(IRestApiService restApi) {
482 this.restApi = restApi;
483 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700484
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800485 public void setThreadPoolService(IThreadPoolService tp) {
486 this.threadPool = tp;
487 }
488
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 public void setMastershipService(IControllerRegistryService serviceImpl) {
490 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700491 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700492
493 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
494 this.linkDiscovery = linkDiscovery;
495 }
496
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700497 public void setDebugCounter(IDebugCounterService debugCounters) {
498 this.debugCounters = debugCounters;
499 }
500
501 public void setDebugEvent(IDebugEventService debugEvents) {
502 this.debugEvents = debugEvents;
503 }
504
505 IDebugCounterService getDebugCounter() {
506 return this.debugCounters;
Ray Milkey269ffb92014-04-03 14:43:30 -0700507 }
508
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800509 // **********************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700510 // Role Handling
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800511 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700512
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700513 /** created by ONOS - works with registry service
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800514 */
Jonathan Hartcc957a02013-02-26 10:39:04 -0800515 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700516 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700517 public void controlChanged(long dpidLong, boolean hasControl) {
518 Dpid dpid = new Dpid(dpidLong);
Ray Milkey269ffb92014-04-03 14:43:30 -0700519 log.info("Role change callback for switch {}, hasControl {}",
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700520 dpid, hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800521
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800522 Role role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700523
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700524 /*
525 * issue #229
526 * Cannot rely on sw.getRole() as it can be behind due to pending
527 * role changes in the queue. Just submit it and late the
528 * RoleChanger handle duplicates.
529 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700530
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700531 if (hasControl) {
532 role = Role.MASTER;
Ray Milkey269ffb92014-04-03 14:43:30 -0700533 } else {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700534 role = Role.EQUAL; // treat the same as Role.SLAVE
Ray Milkey269ffb92014-04-03 14:43:30 -0700535 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700536
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700537 OFChannelHandler swCh = connectedSwitches.get(dpid.value());
538 if (swCh == null) {
539 log.warn("Switch {} not found in connected switches", dpid);
540 return;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700541 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700542
543 log.debug("Sending role request {} msg to {}", role, dpid);
544 swCh.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800545 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700546 }
547
548 public synchronized void submitRegistryRequest(long dpid) {
549 OFChannelHandler h = connectedSwitches.get(dpid);
550 if (h == null) {
551 log.error("Trying to request registry control for switch {} "
552 + "not in connected switches. Aborting.. ",
553 HexString.toHexString(dpid));
554 // FIXME shouldn't we immediately return here?
555 }
556 //Request control of the switch from the global registry
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800557 try {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700558 h.controlRequested = Boolean.TRUE;
559 registryService.requestControl(dpid, new RoleChangeCallback());
560 } catch (RegistryException e) {
561 log.debug("Registry error: {}", e.getMessage());
562 h.controlRequested = Boolean.FALSE;
563 }
564 if (!h.controlRequested) { // XXX what is being attempted here?
565 // yield to allow other thread(s) to release control
566 try {
567 Thread.sleep(10);
568 } catch (InterruptedException e) {
569 // Ignore interruptions
570 }
571 // safer to bounce the switch to reconnect here than proceeding further
572 // XXX S why? can't we just try again a little later?
573 log.debug("Closing sw:{} because we weren't able to request control " +
574 "successfully" + dpid);
575 connectedSwitches.get(dpid).disconnectSwitch();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800576 }
577 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700578
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700579 public synchronized void releaseRegistryControl(long dpidLong) {
580 OFChannelHandler h = connectedSwitches.get(dpidLong);
581 if (h == null) {
582 log.error("Trying to release registry control for switch {} "
583 + "not in connected switches. Aborting.. ",
584 HexString.toHexString(dpidLong));
585 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800586 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700587 if (h.controlRequested) {
588 registryService.releaseControl(dpidLong);
589 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800590 }
591
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700592
593 // *******************
594 // OF Message Handling
595 // *******************
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800596
597 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700598 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700599 * Handle and dispatch a message to IOFMessageListeners.
600 *
601 * We only dispatch messages to listeners if the controller's role is MASTER.
602 *
603 * @param sw The switch sending the message
604 * @param m The message the switch sent
605 * @param flContext The floodlight context to use for this message. If
606 * null, a new context will be allocated.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800607 * @throws IOException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700608 *
609 * FIXME: this method and the ChannelHandler disagree on which messages
610 * should be dispatched and which shouldn't
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800611 */
612 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700613 @LogMessageDoc(level = "ERROR",
614 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
615 " field is empty.",
616 explanation = "The switch sent an improperly-formatted PacketIn" +
617 " message",
618 recommendation = LogMessageDoc.CHECK_SWITCH),
619 @LogMessageDoc(level = "WARN",
620 message = "Unhandled OF Message: {} from {}",
621 explanation = "The switch sent a message not handled by " +
622 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623 })
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700624 @SuppressWarnings({ "fallthrough", "unchecked" })
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800625 protected void handleMessage(IOFSwitch sw, OFMessage m,
626 FloodlightContext bContext)
627 throws IOException {
628 Ethernet eth = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700629 short inport = -1;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800630
631 switch (m.getType()) {
632 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -0700633 OFPacketIn pi = (OFPacketIn) m;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700634 //log.info("saw packet in from sw {}", sw.getStringId());
635 if (pi.getData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700636 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700637 ") because/* the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800638 return;
639 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700640
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700641 // get incoming port to store in floodlight context
642 if (sw.getOFVersion() == OFVersion.OF_10) {
643 inport = pi.getInPort().getShortPortNumber();
644 } else if (sw.getOFVersion() == OFVersion.OF_13) {
645 for (MatchField<?> mf : pi.getMatch().getMatchFields()) {
646 if (mf.id == MatchFields.IN_PORT) {
647 inport = pi.getMatch().get((MatchField<OFPort>)mf)
648 .getShortPortNumber();
649 break;
650 }
651 }
652 if (inport == -1) {
653 log.error("Match field for incoming port missing in "
654 + "packet-in from sw {} .. Ignoring msg",
655 sw.getStringId());
656 return;
657 }
658 } else {
659 // should have been taken care of earlier in handshake
660 log.error("OFVersion {} not supported for "
661 + "packet-in from sw {} .. Ignoring msg",
662 sw.getOFVersion(), sw.getStringId());
663 return;
664 }
665
666 // decode enclosed ethernet packet to store in floodlight context
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800667 if (Controller.ALWAYS_DECODE_ETH) {
668 eth = new Ethernet();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700669 eth.deserialize(pi.getData(), 0,
670 pi.getData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800671 }
672 // fall through to default case...
673
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700674 /*log.debug("Sw:{} packet-in: {}", sw.getStringId(),
675 String.format("0x%x", eth.getEtherType()));*/
676 if (eth.getEtherType() != (short)EthType.LLDP.getValue())
677 log.trace("Sw:{} packet-in: {}", sw.getStringId(), pi);
678
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800679 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700680
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800681 List<IOFMessageListener> listeners = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700682
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800683 if (messageListeners.containsKey(m.getType())) {
684 listeners = messageListeners.get(m.getType()).
685 getOrderedListeners();
686 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800687 FloodlightContext bc = null;
688 if (listeners != null) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700689 // Check if floodlight context is passed from the calling
690 // function, if so use that floodlight context, otherwise
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800691 // allocate one
692 if (bContext == null) {
693 bc = flcontext_alloc();
694 } else {
695 bc = bContext;
696 }
697 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700698 IFloodlightProviderService.bcStore.put(bc,
699 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800700 eth);
701 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700702 if (inport != -1) {
703 bc.getStorage().put(
704 IFloodlightProviderService.CONTEXT_PI_INPORT,
705 inport);
706 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700707
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700708 // Get the starting time (overall and per-component) of
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800709 // the processing chain for this packet if performance
710 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -0700711
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700712 Command cmd = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800713 for (IOFMessageListener listener : listeners) {
714 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700715 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800716 continue;
717 }
718 }
719
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800720 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -0800721
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800722 if (Command.STOP.equals(cmd)) {
723 break;
724 }
725 }
mininet73e7fb72013-12-03 14:25:53 -0800726
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800727 } else {
728 log.warn("Unhandled OF Message: {} from {}", m, sw);
729 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700730
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800731 if ((bContext == null) && (bc != null)) flcontext_free(bc);
732 }
733 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700734
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800735 // ***************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700736 // IFloodlightProviderService
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800737 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700738
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700739 // FIXME: remove this method
740 @Override
741 public Map<Long,IOFSwitch> getSwitches() {
742 return getMasterSwitches();
743 }
744
745 // FIXME: remove this method
746 public Map<Long, IOFSwitch> getMasterSwitches() {
747 return Collections.unmodifiableMap(activeMasterSwitches);
748 }
749
750
751 @Override
752 public Set<Long> getAllSwitchDpids() {
753 Set<Long> dpids = new HashSet<Long>();
754 dpids.addAll(activeMasterSwitches.keySet());
755 dpids.addAll(activeEqualSwitches.keySet());
756 return dpids;
757 }
758
759 @Override
760 public Set<Long> getAllMasterSwitchDpids() {
761 Set<Long> dpids = new HashSet<Long>();
762 dpids.addAll(activeMasterSwitches.keySet());
763 return dpids;
764 }
765
766 @Override
767 public Set<Long> getAllEqualSwitchDpids() {
768 Set<Long> dpids = new HashSet<Long>();
769 dpids.addAll(activeEqualSwitches.keySet());
770 return dpids;
771 }
772
773 @Override
774 public IOFSwitch getSwitch(long dpid) {
775 IOFSwitch sw = null;
776 if ((sw = activeMasterSwitches.get(dpid)) != null) return sw;
777 if ((sw = activeEqualSwitches.get(dpid)) != null) return sw;
778 return sw;
779 }
780
781 @Override
782 public IOFSwitch getMasterSwitch(long dpid) {
783 return activeMasterSwitches.get(dpid);
784 }
785
786 @Override
787 public IOFSwitch getEqualSwitch(long dpid) {
788 return activeEqualSwitches.get(dpid);
789 }
790
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800791 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700792 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800793 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700794 ListenerDispatcher<OFType, IOFMessageListener> ldd =
795 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800796 if (ldd == null) {
797 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
798 messageListeners.put(type, ldd);
799 }
800 ldd.addListener(type, listener);
801 }
802
803 @Override
804 public synchronized void removeOFMessageListener(OFType type,
805 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700806 ListenerDispatcher<OFType, IOFMessageListener> ldd =
807 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800808 if (ldd != null) {
809 ldd.removeListener(listener);
810 }
811 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700812
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700813 public void removeOFMessageListeners(OFType type) {
814 messageListeners.remove(type);
815 }
816
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800817 private void logListeners() {
818 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700819 ListenerDispatcher<OFType,
820 IOFMessageListener>> entry
821 : messageListeners.entrySet()) {
822
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800823 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -0700824 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800825 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -0700826
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800827 StringBuffer sb = new StringBuffer();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700828 sb.append("OFMessageListeners for ");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800829 sb.append(type);
830 sb.append(": ");
831 for (IOFMessageListener l : ldd.getOrderedListeners()) {
832 sb.append(l.getName());
833 sb.append(",");
834 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700835 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800836 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700837 StringBuffer sl = new StringBuffer();
838 sl.append("SwitchUpdate Listeners: ");
839 for (IOFSwitchListener swlistener : switchListeners) {
840 sl.append(swlistener.getName());
841 sl.append(",");
842 }
843 log.debug(sl.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -0700844
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800845 }
846
847 @Override
848 public void addOFSwitchListener(IOFSwitchListener listener) {
849 this.switchListeners.add(listener);
850 }
851
852 @Override
853 public void removeOFSwitchListener(IOFSwitchListener listener) {
854 this.switchListeners.remove(listener);
855 }
856
857 @Override
858 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700859 Map<OFType, List<IOFMessageListener>> lers =
860 new HashMap<OFType, List<IOFMessageListener>>();
861 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
862 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800863 lers.put(e.getKey(), e.getValue().getOrderedListeners());
864 }
865 return Collections.unmodifiableMap(lers);
866 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700867
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700868 /*@Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800869 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700870 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
871 "a null switch",
872 explanation = "Failed to process a message because the switch " +
873 " is no longer connected."),
874 @LogMessageDoc(level = "ERROR",
875 message = "Error reinjecting OFMessage on switch {switch}",
876 explanation = "An I/O error occured while attempting to " +
877 "process an OpenFlow message",
878 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800879 })
880 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
881 FloodlightContext bc) {
882 if (sw == null) {
883 log.info("Failed to inject OFMessage {} onto a null switch", msg);
884 return false;
885 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700886
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800887 // FIXME: Do we need to be able to inject messages to switches
888 // where we're the slave controller (i.e. they're connected but
889 // not active)?
890 // FIXME: Don't we need synchronization logic here so we're holding
891 // the listener read lock when we call handleMessage? After some
892 // discussions it sounds like the right thing to do here would be to
893 // inject the message as a netty upstream channel event so it goes
894 // through the normal netty event processing, including being
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700895 // handled
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800896 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700897
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800898 try {
899 // Pass Floodlight context to the handleMessages()
900 handleMessage(sw, msg, bc);
901 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700902 log.error("Error reinjecting OFMessage on switch {}",
903 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800904 return false;
905 }
906 return true;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700907 }*/
908
909
910
911// @Override
912// public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
913// // call the overloaded version with floodlight context set to null
914// return injectOfMessage(sw, msg, null);
915// }
916
917// @Override
918// public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
919// FloodlightContext bc) {
920//
921// List<IOFMessageListener> listeners = null;
922// if (messageListeners.containsKey(m.getType())) {
923// listeners =
924// messageListeners.get(m.getType()).getOrderedListeners();
925// }
926//
927// if (listeners != null) {
928// for (IOFMessageListener listener : listeners) {
929// if (listener instanceof IOFSwitchFilter) {
930// if (!((IOFSwitchFilter) listener).isInterested(sw)) {
931// continue;
932// }
933// }
934// if (Command.STOP.equals(listener.receive(sw, m, bc))) {
935// break;
936// }
937// }
938// }
939// }
940
941 @Override
942 public OFFactory getOFMessageFactory_10() {
943 return factory10;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800944 }
945
946 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700947 public OFFactory getOFMessageFactory_13() {
948 return factory13;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800949 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700950
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800951 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700952 public void publishUpdate(IUpdate update) {
953 try {
954 this.updates.put(update);
955 } catch (InterruptedException e) {
956 log.error("Failure adding update to queue", e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800957 }
958 }
959
960 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700961 public Map<String, String> getControllerNodeIPs() {
962 // We return a copy of the mapping so we can guarantee that
963 // the mapping return is the same as one that will be (or was)
964 // dispatched to IHAListeners
965 HashMap<String, String> retval = new HashMap<String, String>();
966 synchronized (controllerNodeIPsCache) {
967 retval.putAll(controllerNodeIPsCache);
968 }
969 return retval;
970 }
971
972 @Override
973 public long getSystemStartTime() {
974 return (this.systemStartTime);
975 }
976
977 @Override
978 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
979 this.alwaysClearFlowsOnSwAdd = value;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800980 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700981
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800982 @Override
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -0700983 public OnosInstanceId getOnosInstanceId() {
984 return onosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800985 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700986
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700987 /**
988 * FOR TESTING ONLY. Dispatch all updates in the update queue until queue is
989 * empty
990 */
991 void processUpdateQueueForTesting() {
992 while (!updates.isEmpty()) {
993 IUpdate update = updates.poll();
994 if (update != null)
995 update.dispatch();
996 }
997 }
998
999
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001000 // **************
1001 // Initialization
1002 // **************
1003
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001004 // XXX S This should probably go away OR it should be edited to handle
1005 // controller roles per switch! Then it could be a way to
1006 // deterministically configure a switch to a MASTER controller instance
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001007 /**
1008 * Sets the initial role based on properties in the config params.
1009 * It looks for two different properties.
1010 * If the "role" property is specified then the value should be
1011 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1012 * controller is set to the specified value. If the "role" property
1013 * is not specified then it looks next for the "role.path" property.
1014 * In this case the value should be the path to a property file in
1015 * the file system that contains a property called "floodlight.role"
1016 * which can be one of the values listed above for the "role" property.
1017 * The idea behind the "role.path" mechanism is that you have some
1018 * separate heartbeat and master controller election algorithm that
1019 * determines the role of the controller. When a role transition happens,
1020 * it updates the current role in the file specified by the "role.path"
1021 * file. Then if floodlight restarts for some reason it can get the
1022 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001023 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001024 * @param configParams The config params for the FloodlightProvider service
1025 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001026 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001027 */
1028 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001029 @LogMessageDoc(message = "Controller role set to {role}",
1030 explanation = "Setting the initial HA role to "),
1031 @LogMessageDoc(level = "ERROR",
1032 message = "Invalid current role value: {role}",
1033 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001034 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001035 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001036 })
1037 protected Role getInitialRole(Map<String, String> configParams) {
1038 Role role = null;
1039 String roleString = configParams.get("role");
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001040 FileInputStream fs = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001041 if (roleString == null) {
1042 String rolePath = configParams.get("rolepath");
1043 if (rolePath != null) {
1044 Properties properties = new Properties();
1045 try {
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001046 fs = new FileInputStream(rolePath);
1047 properties.load(fs);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001048 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001049 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001050 // Don't treat it as an error if the file specified by the
1051 // rolepath property doesn't exist. This lets us enable the
1052 // HA mechanism by just creating/setting the floodlight.role
1053 // property in that file without having to modify the
1054 // floodlight properties.
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001055 } finally {
1056 if (fs != null) {
1057 try {
1058 fs.close();
1059 } catch (IOException e) {
1060 log.error("Exception while closing resource ", e);
1061 }
1062 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001063 }
1064 }
1065 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001066
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001067 if (roleString != null) {
1068 // Canonicalize the string to the form used for the enum constants
1069 roleString = roleString.trim().toUpperCase();
1070 try {
1071 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001072 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001073 log.error("Invalid current role value: {}", roleString);
1074 }
1075 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001076
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001077 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001078
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001079 return role;
1080 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001081
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001082 /**
1083 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001084 *
1085 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001086 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001087 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001088 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001089 @LogMessageDoc(message = "Listening for switch connections on {address}",
1090 explanation = "The controller is ready and listening for new" +
1091 " switch connections"),
1092 @LogMessageDoc(message = "Storage exception in controller " +
1093 "updates loop; terminating process",
1094 explanation = ERROR_DATABASE,
1095 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1096 @LogMessageDoc(level = "ERROR",
1097 message = "Exception in controller updates loop",
1098 explanation = "Failed to dispatch controller event",
1099 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001100 })
1101 public void run() {
1102 if (log.isDebugEnabled()) {
1103 logListeners();
1104 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001105
1106 try {
1107 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001108
1109 bootstrap.setOption("reuseAddr", true);
1110 bootstrap.setOption("child.keepAlive", true);
1111 bootstrap.setOption("child.tcpNoDelay", true);
1112 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1113
Ray Milkey269ffb92014-04-03 14:43:30 -07001114 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001115 new OpenflowPipelineFactory(this, null);
1116 bootstrap.setPipelineFactory(pfact);
1117 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1118 final ChannelGroup cg = new DefaultChannelGroup();
1119 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001120
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001121 log.info("Listening for switch connections on {}", sa);
1122 } catch (Exception e) {
1123 throw new RuntimeException(e);
1124 }
1125
1126 // main loop
1127 while (true) {
1128 try {
1129 IUpdate update = updates.take();
1130 update.dispatch();
1131 } catch (InterruptedException e) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001132 log.error("Received interrupted exception in updates loop;" +
1133 "terminating process");
1134 terminate();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001135 } catch (Exception e) {
1136 log.error("Exception in controller updates loop", e);
1137 }
1138 }
1139 }
1140
1141 private ServerBootstrap createServerBootStrap() {
1142 if (workerThreads == 0) {
1143 return new ServerBootstrap(
1144 new NioServerSocketChannelFactory(
1145 Executors.newCachedThreadPool(),
1146 Executors.newCachedThreadPool()));
1147 } else {
1148 return new ServerBootstrap(
1149 new NioServerSocketChannelFactory(
1150 Executors.newCachedThreadPool(),
1151 Executors.newCachedThreadPool(), workerThreads));
1152 }
1153 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001154
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001155 public void setConfigParams(Map<String, String> configParams) {
1156 String ofPort = configParams.get("openflowport");
1157 if (ofPort != null) {
1158 this.openFlowPort = Integer.parseInt(ofPort);
1159 }
1160 log.debug("OpenFlow port set to {}", this.openFlowPort);
1161 String threads = configParams.get("workerthreads");
1162 if (threads != null) {
1163 this.workerThreads = Integer.parseInt(threads);
1164 }
1165 log.debug("Number of worker threads set to {}", this.workerThreads);
1166 String controllerId = configParams.get("controllerid");
1167 if (controllerId != null) {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001168 this.onosInstanceId = new OnosInstanceId(controllerId);
Ray Milkey269ffb92014-04-03 14:43:30 -07001169 } else {
1170 //Try to get the hostname of the machine and use that for controller ID
1171 try {
1172 String hostname = java.net.InetAddress.getLocalHost().getHostName();
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001173 this.onosInstanceId = new OnosInstanceId(hostname);
Ray Milkey269ffb92014-04-03 14:43:30 -07001174 } catch (UnknownHostException e) {
1175 // Can't get hostname, we'll just use the default
1176 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001177 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001178
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001179 log.debug("ControllerId set to {}", this.onosInstanceId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001180 }
1181
Ray Milkey269ffb92014-04-03 14:43:30 -07001182
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001183 /**
1184 * Initialize internal data structures
1185 */
1186 public void init(Map<String, String> configParams) {
1187 // These data structures are initialized here because other
1188 // module's startUp() might be called before ours
1189 this.messageListeners =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001190 new ConcurrentHashMap<OFType, ListenerDispatcher<OFType,
1191 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001192 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001193 this.activeMasterSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1194 this.activeEqualSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1195 this.connectedSwitches = new ConcurrentHashMap<Long, OFChannelHandler>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001196 this.controllerNodeIPsCache = new HashMap<String, String>();
1197 this.updates = new LinkedBlockingQueue<IUpdate>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001198
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001199 setConfigParams(configParams);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001200 this.systemStartTime = System.currentTimeMillis();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001201 this.counters = new Counters();
1202 this.multiCacheLock = new Object();
1203
1204 String option = configParams.get("flushSwitchesOnReconnect");
1205 if (option != null && option.equalsIgnoreCase("true")) {
1206 this.setAlwaysClearFlowsOnSwActivate(true);
1207 log.info("Flush switches on reconnect -- Enabled.");
1208 } else {
1209 this.setAlwaysClearFlowsOnSwActivate(false);
1210 log.info("Flush switches on reconnect -- Disabled");
1211 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001212 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001213
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001214 /**
1215 * Startup all of the controller's components
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001216 * @throws FloodlightModuleException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001217 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001218 @LogMessageDoc(message = "Waiting for storage source",
1219 explanation = "The system database is not yet ready",
1220 recommendation = "If this message persists, this indicates " +
1221 "that the system database has failed to start. " +
1222 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001223 public void startupComponents() throws FloodlightModuleException {
Ray Milkey269ffb92014-04-03 14:43:30 -07001224 try {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001225 registryService.registerController(onosInstanceId.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -07001226 } catch (RegistryException e) {
1227 log.warn("Registry service error: {}", e.getMessage());
1228 }
1229
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001230 // Add our REST API
1231 restApi.addRestletRoutable(new CoreWebRoutable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001232
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001233 // Startup load monitoring
1234 if (overload_drop) {
1235 this.loadmonitor.startMonitoring(
1236 this.threadPool.getScheduledExecutor());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001237 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001238
1239 // register counters and events
1240 try {
1241 this.counters.createCounters(debugCounters);
1242 } catch (CounterException e) {
1243 throw new FloodlightModuleException(e.getMessage());
1244 }
1245 registerControllerDebugEvents();
1246 }
1247
1248 // **************
1249 // debugCounter registrations
1250 // **************
1251
1252 public static class Counters {
1253 public static final String prefix = "controller";
1254 public IDebugCounter setRoleEqual;
1255 public IDebugCounter setSameRole;
1256 public IDebugCounter setRoleMaster;
1257 public IDebugCounter remoteStoreNotification;
1258 public IDebugCounter invalidPortsChanged;
1259 public IDebugCounter invalidSwitchActivatedWhileSlave;
1260 public IDebugCounter invalidStoreEventWhileMaster;
1261 public IDebugCounter switchDisconnectedWhileSlave;
1262 public IDebugCounter switchActivated;
1263 public IDebugCounter errorSameSwitchReactivated; // err
1264 public IDebugCounter switchWithSameDpidActivated; // warn
1265 public IDebugCounter newSwitchActivated; // new switch
1266 public IDebugCounter syncedSwitchActivated;
1267 public IDebugCounter readyForReconcile;
1268 public IDebugCounter newSwitchFromStore;
1269 public IDebugCounter updatedSwitchFromStore;
1270 public IDebugCounter switchDisconnected;
1271 public IDebugCounter syncedSwitchRemoved;
1272 public IDebugCounter unknownSwitchRemovedFromStore;
1273 public IDebugCounter consolidateStoreRunCount;
1274 public IDebugCounter consolidateStoreInconsistencies;
1275 public IDebugCounter storeSyncError;
1276 public IDebugCounter switchesNotReconnectingToNewMaster;
1277 public IDebugCounter switchPortChanged;
1278 public IDebugCounter switchOtherChange;
1279 public IDebugCounter dispatchMessageWhileSlave;
1280 public IDebugCounter dispatchMessage; // does this cnt make sense? more specific?? per type? count stops?
1281 public IDebugCounter controllerNodeIpsChanged;
1282 public IDebugCounter messageReceived;
1283 public IDebugCounter messageInputThrottled;
1284 public IDebugCounter switchDisconnectReadTimeout;
1285 public IDebugCounter switchDisconnectHandshakeTimeout;
1286 public IDebugCounter switchDisconnectIOError;
1287 public IDebugCounter switchDisconnectParseError;
1288 public IDebugCounter switchDisconnectSwitchStateException;
1289 public IDebugCounter rejectedExecutionException;
1290 public IDebugCounter switchDisconnectOtherException;
1291 public IDebugCounter switchConnected;
1292 public IDebugCounter unhandledMessage;
1293 public IDebugCounter packetInWhileSwitchIsSlave;
1294 public IDebugCounter epermErrorWhileSwitchIsMaster;
1295 public IDebugCounter roleNotResentBecauseRolePending;
1296 public IDebugCounter roleRequestSent;
1297 public IDebugCounter roleReplyTimeout;
1298 public IDebugCounter roleReplyReceived; // expected RoleReply received
1299 public IDebugCounter roleReplyErrorUnsupported;
1300 public IDebugCounter switchCounterRegistrationFailed;
1301 public IDebugCounter packetParsingError;
1302
1303 void createCounters(IDebugCounterService debugCounters) throws CounterException {
1304 setRoleEqual =
1305 debugCounters.registerCounter(
1306 prefix, "set-role-equal",
1307 "Controller received a role request with role of "+
1308 "EQUAL which is unusual",
1309 CounterType.ALWAYS_COUNT);
1310 setSameRole =
1311 debugCounters.registerCounter(
1312 prefix, "set-same-role",
1313 "Controller received a role request for the same " +
1314 "role the controller already had",
1315 CounterType.ALWAYS_COUNT,
1316 IDebugCounterService.CTR_MDATA_WARN);
1317
1318 setRoleMaster =
1319 debugCounters.registerCounter(
1320 prefix, "set-role-master",
1321 "Controller received a role request with role of " +
1322 "MASTER. This counter can be at most 1.",
1323 CounterType.ALWAYS_COUNT);
1324
1325 remoteStoreNotification =
1326 debugCounters.registerCounter(
1327 prefix, "remote-store-notification",
1328 "Received a notification from the sync service " +
1329 "indicating that switch information has changed",
1330 CounterType.ALWAYS_COUNT);
1331
1332 invalidPortsChanged =
1333 debugCounters.registerCounter(
1334 prefix, "invalid-ports-changed",
1335 "Received an unexpected ports changed " +
1336 "notification while the controller was in " +
1337 "SLAVE role.",
1338 CounterType.ALWAYS_COUNT,
1339 IDebugCounterService.CTR_MDATA_WARN);
1340
1341 invalidSwitchActivatedWhileSlave =
1342 debugCounters.registerCounter(
1343 prefix, "invalid-switch-activated-while-slave",
1344 "Received an unexpected switchActivated " +
1345 "notification while the controller was in " +
1346 "SLAVE role.",
1347 CounterType.ALWAYS_COUNT,
1348 IDebugCounterService.CTR_MDATA_WARN);
1349
1350 invalidStoreEventWhileMaster =
1351 debugCounters.registerCounter(
1352 prefix, "invalid-store-event-while-master",
1353 "Received an unexpected notification from " +
1354 "the sync store while the controller was in " +
1355 "MASTER role.",
1356 CounterType.ALWAYS_COUNT,
1357 IDebugCounterService.CTR_MDATA_WARN);
1358
1359 switchDisconnectedWhileSlave =
1360 debugCounters.registerCounter(
1361 prefix, "switch-disconnected-while-slave",
1362 "A switch disconnected and the controller was " +
1363 "in SLAVE role.",
1364 CounterType.ALWAYS_COUNT,
1365 IDebugCounterService.CTR_MDATA_WARN);
1366
1367 switchActivated =
1368 debugCounters.registerCounter(
1369 prefix, "switch-activated",
1370 "A switch connected to this controller is now " +
1371 "in MASTER role",
1372 CounterType.ALWAYS_COUNT);
1373
1374 errorSameSwitchReactivated = // err
1375 debugCounters.registerCounter(
1376 prefix, "error-same-switch-reactivated",
1377 "A switch that was already in active state " +
1378 "was activated again. This indicates a " +
1379 "controller defect",
1380 CounterType.ALWAYS_COUNT,
1381 IDebugCounterService.CTR_MDATA_ERROR);
1382
1383 switchWithSameDpidActivated = // warn
1384 debugCounters.registerCounter(
1385 prefix, "switch-with-same-dpid-activated",
1386 "A switch with the same DPID as another switch " +
1387 "connected to the controller. This can be " +
1388 "caused by multiple switches configured with " +
1389 "the same DPID or by a switch reconnecting very " +
1390 "quickly.",
1391 CounterType.COUNT_ON_DEMAND,
1392 IDebugCounterService.CTR_MDATA_WARN);
1393
1394 newSwitchActivated = // new switch
1395 debugCounters.registerCounter(
1396 prefix, "new-switch-activated",
1397 "A new switch has completed the handshake as " +
1398 "MASTER. The switch was not known to any other " +
1399 "controller in the cluster",
1400 CounterType.ALWAYS_COUNT);
1401 syncedSwitchActivated =
1402 debugCounters.registerCounter(
1403 prefix, "synced-switch-activated",
1404 "A switch has completed the handshake as " +
1405 "MASTER. The switch was known to another " +
1406 "controller in the cluster",
1407 CounterType.ALWAYS_COUNT);
1408
1409 readyForReconcile =
1410 debugCounters.registerCounter(
1411 prefix, "ready-for-reconcile",
1412 "Controller is ready for flow reconciliation " +
1413 "after Slave to Master transition. Either all " +
1414 "previously known switches are now active " +
1415 "or they have timed out and have been removed." +
1416 "This counter will be 0 or 1.",
1417 CounterType.ALWAYS_COUNT);
1418
1419 newSwitchFromStore =
1420 debugCounters.registerCounter(
1421 prefix, "new-switch-from-store",
1422 "A new switch has connected to another " +
1423 "another controller in the cluster. This " +
1424 "controller instance has received a sync store " +
1425 "notification for it.",
1426 CounterType.ALWAYS_COUNT);
1427
1428 updatedSwitchFromStore =
1429 debugCounters.registerCounter(
1430 prefix, "updated-switch-from-store",
1431 "Information about a switch connected to " +
1432 "another controller instance was updated in " +
1433 "the sync store. This controller instance has " +
1434 "received a notification for it",
1435 CounterType.ALWAYS_COUNT);
1436
1437 switchDisconnected =
1438 debugCounters.registerCounter(
1439 prefix, "switch-disconnected",
1440 "FIXME: switch has disconnected",
1441 CounterType.ALWAYS_COUNT);
1442
1443 syncedSwitchRemoved =
1444 debugCounters.registerCounter(
1445 prefix, "synced-switch-removed",
1446 "A switch connected to another controller " +
1447 "instance has disconnected from the controller " +
1448 "cluster. This controller instance has " +
1449 "received a notification for it",
1450 CounterType.ALWAYS_COUNT);
1451
1452 unknownSwitchRemovedFromStore =
1453 debugCounters.registerCounter(
1454 prefix, "unknown-switch-removed-from-store",
1455 "This controller instances has received a sync " +
1456 "store notification that a switch has " +
1457 "disconnected but this controller instance " +
1458 "did not have the any information about the " +
1459 "switch", // might be less than warning
1460 CounterType.ALWAYS_COUNT,
1461 IDebugCounterService.CTR_MDATA_WARN);
1462
1463 consolidateStoreRunCount =
1464 debugCounters.registerCounter(
1465 prefix, "consolidate-store-run-count",
1466 "This controller has transitioned from SLAVE " +
1467 "to MASTER and waited for switches to reconnect. " +
1468 "The controller has finished waiting and has " +
1469 "reconciled switch entries in the sync store " +
1470 "with live state",
1471 CounterType.ALWAYS_COUNT);
1472
1473 consolidateStoreInconsistencies =
1474 debugCounters.registerCounter(
1475 prefix, "consolidate-store-inconsistencies",
1476 "During switch sync store consolidation: " +
1477 "Number of switches that were in the store " +
1478 "but not otherwise known plus number of " +
1479 "switches that were in the store previously " +
1480 "but are now missing plus number of " +
1481 "connected switches that were absent from " +
1482 "the store although this controller has " +
1483 "written them. A non-zero count " +
1484 "indicates a brief split-brain dual MASTER " +
1485 "situation during fail-over",
1486 CounterType.ALWAYS_COUNT);
1487
1488 storeSyncError =
1489 debugCounters.registerCounter(
1490 prefix, "store-sync-error",
1491 "Number of times a sync store operation failed " +
1492 "due to a store sync exception or an entry in " +
1493 "in the store had invalid data.",
1494 CounterType.ALWAYS_COUNT,
1495 IDebugCounterService.CTR_MDATA_ERROR);
1496
1497 switchesNotReconnectingToNewMaster =
1498 debugCounters.registerCounter(
1499 prefix, "switches-not-reconnecting-to-new-master",
1500 "Switches that were connected to another " +
1501 "controller instance in the cluster but that " +
1502 "did not reconnect to this controller after it " +
1503 "transitioned to MASTER", // might be less than warning
1504 CounterType.ALWAYS_COUNT);
1505
1506 switchPortChanged =
1507 debugCounters.registerCounter(
1508 prefix, "switch-port-changed",
1509 "Number of times switch ports have changed",
1510 CounterType.ALWAYS_COUNT);
1511 switchOtherChange =
1512 debugCounters.registerCounter(
1513 prefix, "switch-other-change",
1514 "Number of times other information of a switch " +
1515 "has changed.",
1516 CounterType.ALWAYS_COUNT);
1517
1518 dispatchMessageWhileSlave =
1519 debugCounters.registerCounter(
1520 prefix, "dispatch-message-while-slave",
1521 "Number of times an OF message was received " +
1522 "and supposed to be dispatched but the " +
1523 "controller was in SLAVE role and the message " +
1524 "was not dispatched",
1525 CounterType.ALWAYS_COUNT);
1526
1527 dispatchMessage = // does this cnt make sense? more specific?? per type? count stops?
1528 debugCounters.registerCounter(
1529 prefix, "dispatch-message",
1530 "Number of times an OF message was dispatched " +
1531 "to registered modules",
1532 CounterType.ALWAYS_COUNT);
1533
1534 controllerNodeIpsChanged =
1535 debugCounters.registerCounter(
1536 prefix, "controller-nodes-ips-changed",
1537 "IP addresses of controller nodes have changed",
1538 CounterType.ALWAYS_COUNT);
1539
1540 //------------------------
1541 // channel handler counters. Factor them out ??
1542 messageReceived =
1543 debugCounters.registerCounter(
1544 prefix, "message-received",
1545 "Number of OpenFlow messages received. Some of " +
1546 "these might be throttled",
1547 CounterType.ALWAYS_COUNT);
1548 messageInputThrottled =
1549 debugCounters.registerCounter(
1550 prefix, "message-input-throttled",
1551 "Number of OpenFlow messages that were " +
1552 "throttled due to high load from the sender",
1553 CounterType.ALWAYS_COUNT,
1554 IDebugCounterService.CTR_MDATA_WARN);
1555 // TODO: more counters in messageReceived ??
1556
1557 switchDisconnectReadTimeout =
1558 debugCounters.registerCounter(
1559 prefix, "switch-disconnect-read-timeout",
1560 "Number of times a switch was disconnected due " +
1561 "due the switch failing to send OpenFlow " +
1562 "messages or responding to OpenFlow ECHOs",
1563 CounterType.ALWAYS_COUNT,
1564 IDebugCounterService.CTR_MDATA_ERROR);
1565 switchDisconnectHandshakeTimeout =
1566 debugCounters.registerCounter(
1567 prefix, "switch-disconnect-handshake-timeout",
1568 "Number of times a switch was disconnected " +
1569 "because it failed to complete the handshake " +
1570 "in time.",
1571 CounterType.ALWAYS_COUNT,
1572 IDebugCounterService.CTR_MDATA_ERROR);
1573 switchDisconnectIOError =
1574 debugCounters.registerCounter(
1575 prefix, "switch-disconnect-io-error",
1576 "Number of times a switch was disconnected " +
1577 "due to IO errors on the switch connection.",
1578 CounterType.ALWAYS_COUNT,
1579 IDebugCounterService.CTR_MDATA_ERROR);
1580 switchDisconnectParseError =
1581 debugCounters.registerCounter(
1582 prefix, "switch-disconnect-parse-error",
1583 "Number of times a switch was disconnected " +
1584 "because it sent an invalid packet that could " +
1585 "not be parsed",
1586 CounterType.ALWAYS_COUNT,
1587 IDebugCounterService.CTR_MDATA_ERROR);
1588
1589 switchDisconnectSwitchStateException =
1590 debugCounters.registerCounter(
1591 prefix, "switch-disconnect-switch-state-exception",
1592 "Number of times a switch was disconnected " +
1593 "because it sent messages that were invalid " +
1594 "given the switch connection's state.",
1595 CounterType.ALWAYS_COUNT,
1596 IDebugCounterService.CTR_MDATA_ERROR);
1597 rejectedExecutionException =
1598 debugCounters.registerCounter(
1599 prefix, "rejected-execution-exception",
1600 "TODO",
1601 CounterType.ALWAYS_COUNT,
1602 IDebugCounterService.CTR_MDATA_ERROR);
1603
1604 switchDisconnectOtherException =
1605 debugCounters.registerCounter(
1606 prefix, "switch-disconnect-other-exception",
1607 "Number of times a switch was disconnected " +
1608 "due to an exceptional situation not covered " +
1609 "by other counters",
1610 CounterType.ALWAYS_COUNT,
1611 IDebugCounterService.CTR_MDATA_ERROR);
1612
1613 switchConnected =
1614 debugCounters.registerCounter(
1615 prefix, "switch-connected",
1616 "Number of times a new switch connection was " +
1617 "established",
1618 CounterType.ALWAYS_COUNT);
1619
1620 unhandledMessage =
1621 debugCounters.registerCounter(
1622 prefix, "unhandled-message",
1623 "Number of times an OpenFlow message was " +
1624 "received that the controller ignored because " +
1625 "it was inapproriate given the switch " +
1626 "connection's state.",
1627 CounterType.ALWAYS_COUNT,
1628 IDebugCounterService.CTR_MDATA_WARN);
1629 // might be less than warning
1630
1631 packetInWhileSwitchIsSlave =
1632 debugCounters.registerCounter(
1633 prefix, "packet-in-while-switch-is-slave",
1634 "Number of times a packet in was received " +
1635 "from a switch that was in SLAVE role. " +
1636 "Possibly inidicates inconsistent roles.",
1637 CounterType.ALWAYS_COUNT);
1638 epermErrorWhileSwitchIsMaster =
1639 debugCounters.registerCounter(
1640 prefix, "eperm-error-while-switch-is-master",
1641 "Number of times a permission error was " +
1642 "received while the switch was in MASTER role. " +
1643 "Possibly inidicates inconsistent roles.",
1644 CounterType.ALWAYS_COUNT,
1645 IDebugCounterService.CTR_MDATA_WARN);
1646
1647 roleNotResentBecauseRolePending =
1648 debugCounters.registerCounter(
1649 prefix, "role-not-resent-because-role-pending",
1650 "The controller tried to reestablish a role " +
1651 "with a switch but did not do so because a " +
1652 "previous role request was still pending",
1653 CounterType.ALWAYS_COUNT);
1654 roleRequestSent =
1655 debugCounters.registerCounter(
1656 prefix, "role-request-sent",
1657 "Number of times the controller sent a role " +
1658 "request to a switch.",
1659 CounterType.ALWAYS_COUNT);
1660 roleReplyTimeout =
1661 debugCounters.registerCounter(
1662 prefix, "role-reply-timeout",
1663 "Number of times a role request message did not " +
1664 "receive the expected reply from a switch",
1665 CounterType.ALWAYS_COUNT,
1666 IDebugCounterService.CTR_MDATA_WARN);
1667
1668 roleReplyReceived = // expected RoleReply received
1669 debugCounters.registerCounter(
1670 prefix, "role-reply-received",
1671 "Number of times the controller received the " +
1672 "expected role reply message from a switch",
1673 CounterType.ALWAYS_COUNT);
1674
1675 roleReplyErrorUnsupported =
1676 debugCounters.registerCounter(
1677 prefix, "role-reply-error-unsupported",
1678 "Number of times the controller received an " +
1679 "error from a switch in response to a role " +
1680 "request indicating that the switch does not " +
1681 "support roles.",
1682 CounterType.ALWAYS_COUNT);
1683
1684 switchCounterRegistrationFailed =
1685 debugCounters.registerCounter(prefix,
1686 "switch-counter-registration-failed",
1687 "Number of times the controller failed to " +
1688 "register per-switch debug counters",
1689 CounterType.ALWAYS_COUNT,
1690 IDebugCounterService.CTR_MDATA_WARN);
1691
1692 packetParsingError =
1693 debugCounters.registerCounter(prefix,
1694 "packet-parsing-error",
1695 "Number of times the packet parsing " +
1696 "encountered an error",
1697 CounterType.ALWAYS_COUNT,
1698 IDebugCounterService.CTR_MDATA_ERROR);
1699 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001700 }
1701
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001702 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001703 public Counters getCounters() {
1704 return this.counters;
1705 }
1706
1707 // **************
1708 // debugEvent registrations
1709 // **************
1710
1711 private void registerControllerDebugEvents() throws FloodlightModuleException {
1712 if (debugEvents == null) {
1713 debugEvents = new NullDebugEvent();
1714 }
1715 try {
1716 evSwitch = debugEvents.registerEvent(
1717 Counters.prefix, "switchevent",
1718 "Switch connected, disconnected or port changed",
1719 EventType.ALWAYS_LOG, SwitchEvent.class, 100);
1720 } catch (MaxEventsRegistered e) {
1721 throw new FloodlightModuleException("Max events registered", e);
1722 }
1723 }
1724
1725 public class SwitchEvent {
1726 @EventColumn(name = "dpid", description = EventFieldType.DPID)
1727 long dpid;
1728
1729 @EventColumn(name = "reason", description = EventFieldType.STRING)
1730 String reason;
1731
1732 public SwitchEvent(long dpid, String reason) {
1733 this.dpid = dpid;
1734 this.reason = reason;
1735 }
1736 }
1737
1738 // **************
1739 // Utility methods
1740 // **************
1741
1742 @Override
1743 public void setAlwaysClearFlowsOnSwActivate(boolean value) {
1744 //this.alwaysClearFlowsOnSwActivate = value;
1745 // XXX S need to be a little more careful about this
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001746 }
1747
1748 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001749 public Map<String, Long> getMemory() {
1750 Map<String, Long> m = new HashMap<String, Long>();
1751 Runtime runtime = Runtime.getRuntime();
1752 m.put("total", runtime.totalMemory());
1753 m.put("free", runtime.freeMemory());
1754 return m;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001755 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001756
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001757 @Override
1758 public Long getUptime() {
1759 RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
1760 return rb.getUptime();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001761 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001762
1763 /**
1764 * Forward to the driver-manager to get an IOFSwitch instance.
1765 * @param desc
1766 * @return
1767 */
1768 protected IOFSwitch getOFSwitchInstance(OFDescStatsReply desc, OFVersion ofv) {
1769 return DriverManager.getOFSwitchImpl(desc, ofv);
1770 }
1771
1772 protected IThreadPoolService getThreadPoolService() {
1773 return this.threadPool;
1774 }
1775
1776 /**
1777 * Part of the controller updates framework (see 'run()' method)
1778 * Use this method to add an IUpdate. A thread-pool will serve the update
1779 * by dispatching it to all listeners for that update.
1780 * @param update
1781 */
1782 @LogMessageDoc(level="WARN",
1783 message="Failure adding update {} to queue",
1784 explanation="The controller tried to add an internal notification" +
1785 " to its message queue but the add failed.",
1786 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
1787 private void addUpdateToQueue(IUpdate update) {
1788 try {
1789 this.updates.put(update);
1790 } catch (InterruptedException e) {
1791 // This should never happen
1792 log.error("Failure adding update {} to queue.", update);
1793 }
1794 }
1795
1796 void flushAll() {
1797 // Flush all flow-mods/packet-out/stats generated from this "train"
1798 OFSwitchImplBase.flush_all();
1799 debugCounters.flushCounters();
1800 debugEvents.flushEvents();
1801 }
1802
1803 /**
1804 * flcontext_free - Free the context to the current thread
1805 *
1806 * @param flcontext
1807 */
1808 protected void flcontext_free(FloodlightContext flcontext) {
1809 flcontext.getStorage().clear();
1810 flcontext_cache.get().push(flcontext);
1811 }
1812
1813 @LogMessageDoc(message = "Calling System.exit",
1814 explanation = "The controller is terminating")
1815 private synchronized void terminate() {
1816 log.info("Calling System.exit");
1817 System.exit(1);
1818 }
1819
1820
1821 // ***************
1822 // Floodlight context related
1823 // ***************
1824
1825 /**
1826 * flcontext_cache - Keep a thread local stack of contexts
1827 */
1828 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
1829 new ThreadLocal<Stack<FloodlightContext>>() {
1830 @Override
1831 protected Stack<FloodlightContext> initialValue() {
1832 return new Stack<FloodlightContext>();
1833 }
1834 };
1835
1836 /**
1837 * flcontext_alloc - pop a context off the stack, if required create a new one
1838 *
1839 * @return FloodlightContext
1840 */
1841 protected static FloodlightContext flcontext_alloc() {
1842 FloodlightContext flcontext = null;
1843
1844 if (flcontext_cache.get().empty()) {
1845 flcontext = new FloodlightContext();
1846 } else {
1847 flcontext = flcontext_cache.get().pop();
1848 }
1849
1850 return flcontext;
1851 }
1852
1853
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001854}