Java tutorial
/** * Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. **/ package net.floodlightcontroller.core.internal; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedBlockingQueue; import io.netty.util.HashedWheelTimer; import io.netty.util.Timer; import net.floodlightcontroller.core.ControllerId; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.HAListenerTypeMarker; import net.floodlightcontroller.core.HARole; import net.floodlightcontroller.core.IControllerCompletionListener; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IHAListener; import net.floodlightcontroller.core.IInfoProvider; import net.floodlightcontroller.core.IShutdownService; import net.floodlightcontroller.core.IListener.Command; import net.floodlightcontroller.core.IOFMessageListener; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IOFSwitchListener; import net.floodlightcontroller.core.LogicalOFMessageCategory; import net.floodlightcontroller.core.PortChangeType; import net.floodlightcontroller.core.RoleInfo; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.FloodlightModuleLoader; import net.floodlightcontroller.core.util.ListenerDispatcher; import net.floodlightcontroller.core.web.CoreWebRoutable; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugevent.IDebugEventService; import net.floodlightcontroller.notification.INotificationManager; import net.floodlightcontroller.notification.NotificationManagerFactory; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.TransportPort; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.perfmon.IPktInProcessingTimeService; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.storage.IResultSet; import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.StorageException; import net.floodlightcontroller.threadpool.IThreadPoolService; import org.sdnplatform.sync.ISyncService; import org.sdnplatform.sync.ISyncService.Scope; import org.sdnplatform.sync.error.SyncException; import org.sdnplatform.sync.internal.config.ClusterConfig; import net.floodlightcontroller.util.LoadMonitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; import com.google.common.base.Strings; /** * The main controller class. Handles all setup and network listeners */ public class Controller implements IFloodlightProviderService, IStorageSourceListener, IInfoProvider { protected static final Logger log = LoggerFactory.getLogger(Controller.class); protected static final INotificationManager notifier = NotificationManagerFactory .getNotificationManager(Controller.class); static final String ERROR_DATABASE = "The controller could not communicate with the system database."; protected ConcurrentMap<OFType, ListenerDispatcher<OFType, IOFMessageListener>> messageListeners; // paag protected ConcurrentLinkedQueue<IControllerCompletionListener> completionListeners; // The controllerNodeIPsCache maps Controller IDs to their IP address. // It's only used by handleControllerNodeIPsChanged protected HashMap<String, String> controllerNodeIPsCache; protected ListenerDispatcher<HAListenerTypeMarker, IHAListener> haListeners; protected Map<String, List<IInfoProvider>> providerMap; protected BlockingQueue<IUpdate> updates; protected ControllerCounters counters; protected Timer timer; // Module Loader State private ModuleLoaderState moduleLoaderState; public enum ModuleLoaderState { INIT, STARTUP, COMPLETE } // Module dependencies private IStorageSourceService storageSourceService; private IOFSwitchService switchService; private IDebugCounterService debugCounterService; protected IDebugEventService debugEventService; private IRestApiService restApiService; private IPktInProcessingTimeService pktinProcTimeService; private IThreadPoolService threadPoolService; private ISyncService syncService; private IShutdownService shutdownService; // Configuration options private static TransportPort openFlowPort = TransportPort.of(6653); // new registered OF port number private static Set<IPv4Address> openFlowAddresses = new HashSet<IPv4Address>(); public static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; protected int workerThreads = 16; // The id for this controller node. Should be unique for each controller // node in a controller cluster. protected String controllerId = "my-floodlight-controller"; // This controller's current role that modules can use/query to decide // if they should operate in ACTIVE / STANDBY protected volatile HARole notifiedRole; private static final String INITIAL_ROLE_CHANGE_DESCRIPTION = "Controller startup."; /** * NOTE: roleManager is not 'final' because it's initialized at run time * based on parameters that are only available in init() */ private RoleManager roleManager; // Storage table names protected static final String CONTROLLER_TABLE_NAME = "controller_controller"; protected static final String CONTROLLER_ID = "id"; protected static final String SWITCH_CONFIG_TABLE_NAME = "controller_switchconfig"; protected static final String SWITCH_CONFIG_CORE_SWITCH = "core_switch"; protected static final String CONTROLLER_INTERFACE_TABLE_NAME = "controller_controllerinterface"; protected static final String CONTROLLER_INTERFACE_ID = "id"; protected static final String CONTROLLER_INTERFACE_CONTROLLER_ID = "controller_id"; protected static final String CONTROLLER_INTERFACE_TYPE = "type"; protected static final String CONTROLLER_INTERFACE_NUMBER = "number"; protected static final String CONTROLLER_INTERFACE_DISCOVERED_IP = "discovered_ip"; // FIXME: don't use "forwardingconfig" as table name private static final String FLOW_PRIORITY_TABLE_NAME = "controller_forwardingconfig"; private static final String FLOW_COLUMN_PRIMARY_KEY = "id"; private static final String FLOW_VALUE_PRIMARY_KEY = "forwarding"; private static final String FLOW_COLUMN_ACCESS_PRIORITY = "access_priority"; private static final String FLOW_COLUMN_CORE_PRIORITY = "core_priority"; private static final String[] FLOW_COLUMN_NAMES = new String[] { FLOW_COLUMN_PRIMARY_KEY, FLOW_COLUMN_ACCESS_PRIORITY, FLOW_COLUMN_CORE_PRIORITY }; protected static final boolean ALWAYS_DECODE_ETH = true; // Set of port name prefixes that will be classified as uplink ports, // hence will not be autoportfast. Set<String> uplinkPortPrefixSet; @Override public Set<String> getUplinkPortPrefixSet() { return uplinkPortPrefixSet; } public void setUplinkPortPrefixSet(Set<String> prefixSet) { this.uplinkPortPrefixSet = prefixSet; } @Override public ModuleLoaderState getModuleLoaderState() { return this.moduleLoaderState; } // Load monitor for overload protection protected final boolean overload_drop = Boolean.parseBoolean(System.getProperty("overload_drop", "false")); protected final LoadMonitor loadmonitor = new LoadMonitor(log); private static class NotificationSwitchListener implements IOFSwitchListener { @Override public void switchAdded(DatapathId switchId) { notifier.postNotification("Switch " + switchId + " connected."); } @Override public void switchRemoved(DatapathId switchId) { notifier.postNotification("Switch " + switchId + " disconnected."); } @Override public void switchActivated(DatapathId switchId) { } @Override public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) { String msg = String.format("Switch %s port %s changed: %s", switchId, port.getName(), type.toString()); notifier.postNotification(msg); } @Override public void switchChanged(DatapathId switchId) { } } /** * Updates handled by the main loop */ public interface IUpdate { /** * Calls the appropriate listeners */ public void dispatch(); } /** * Update message indicating * IPs of controllers in controller cluster have changed. */ private class HAControllerNodeIPUpdate implements IUpdate { public final Map<String, String> curControllerNodeIPs; public final Map<String, String> addedControllerNodeIPs; public final Map<String, String> removedControllerNodeIPs; public HAControllerNodeIPUpdate(HashMap<String, String> curControllerNodeIPs, HashMap<String, String> addedControllerNodeIPs, HashMap<String, String> removedControllerNodeIPs) { this.curControllerNodeIPs = curControllerNodeIPs; this.addedControllerNodeIPs = addedControllerNodeIPs; this.removedControllerNodeIPs = removedControllerNodeIPs; } @Override public void dispatch() { if (log.isTraceEnabled()) { log.trace( "Dispatching HA Controller Node IP update " + "curIPs = {}, addedIPs = {}, removedIPs = {}", new Object[] { curControllerNodeIPs, addedControllerNodeIPs, removedControllerNodeIPs }); } if (haListeners != null) { for (IHAListener listener : haListeners.getOrderedListeners()) { listener.controllerNodeIPsChanged(curControllerNodeIPs, addedControllerNodeIPs, removedControllerNodeIPs); } } } } // *************** // Getters/Setters // *************** void setStorageSourceService(IStorageSourceService storageSource) { this.storageSourceService = storageSource; } IStorageSourceService getStorageSourceService() { return this.storageSourceService; } IShutdownService getShutdownService() { return this.shutdownService; } void setShutdownService(IShutdownService shutdownService) { this.shutdownService = shutdownService; } public void setDebugEvent(IDebugEventService debugEvent) { this.debugEventService = debugEvent; } void setDebugCounter(IDebugCounterService debugCounters) { this.debugCounterService = debugCounters; } IDebugCounterService getDebugCounter() { return this.debugCounterService; } void setSyncService(ISyncService syncService) { this.syncService = syncService; } void setPktInProcessingService(IPktInProcessingTimeService pits) { this.pktinProcTimeService = pits; } void setRestApiService(IRestApiService restApi) { this.restApiService = restApi; } void setThreadPoolService(IThreadPoolService tp) { this.threadPoolService = tp; } IThreadPoolService getThreadPoolService() { return this.threadPoolService; } public void setSwitchService(IOFSwitchService switchService) { this.switchService = switchService; } public IOFSwitchService getSwitchService() { return this.switchService; } @Override public int getWorkerThreads() { return this.workerThreads; } @Override public HARole getRole() { return notifiedRole; } @Override public RoleInfo getRoleInfo() { return roleManager.getRoleInfo(); } @Override public void setRole(HARole role, String changeDescription) { roleManager.setRole(role, changeDescription); } // **************** // Message handlers // **************** // Handler for SwitchPortsChanged was here (notifyPortChanged). Handled in OFSwitchManager /** * flcontext_cache - Keep a thread local stack of contexts */ protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache = new ThreadLocal<Stack<FloodlightContext>>() { @Override protected Stack<FloodlightContext> initialValue() { return new Stack<FloodlightContext>(); } }; /** * flcontext_alloc - pop a context off the stack, if required create a new one * @return FloodlightContext */ protected static FloodlightContext flcontext_alloc() { FloodlightContext flcontext = null; if (flcontext_cache.get().empty()) { flcontext = new FloodlightContext(); } else { flcontext = flcontext_cache.get().pop(); } return flcontext; } /** * flcontext_free - Free the context to the current thread * @param flcontext */ protected void flcontext_free(FloodlightContext flcontext) { flcontext.getStorage().clear(); flcontext_cache.get().push(flcontext); } /** * * Handle and dispatch a message to IOFMessageListeners. * * We only dispatch messages to listeners if the controller's role is MASTER. * * @param sw The switch sending the message * @param m The message the switch sent * @param flContext The floodlight context to use for this message. If * null, a new context will be allocated. * @throws IOException * * FIXME: this method and the ChannelHandler disagree on which messages * should be dispatched and which shouldn't */ @Override public void handleMessage(IOFSwitch sw, OFMessage m, FloodlightContext bContext) { Ethernet eth = null; log.trace("Dispatching OFMessage to listeners."); if (this.notifiedRole == HARole.STANDBY) { counters.dispatchMessageWhileStandby.increment(); // We are SLAVE. Do not dispatch messages to listeners. return; } counters.dispatchMessage.increment(); switch (m.getType()) { case PACKET_IN: counters.packetIn.increment(); OFPacketIn pi = (OFPacketIn) m; if (pi.getData().length <= 0) { log.error("Ignoring PacketIn (Xid = " + pi.getXid() + ") because the data field is empty."); return; } if (Controller.ALWAYS_DECODE_ETH) { eth = new Ethernet(); eth.deserialize(pi.getData(), 0, pi.getData().length); } // fall through to default case... default: List<IOFMessageListener> listeners = null; if (messageListeners.containsKey(m.getType())) { listeners = messageListeners.get(m.getType()).getOrderedListeners(); } FloodlightContext bc = null; if (listeners != null) { // Check if floodlight context is passed from the calling // function, if so use that floodlight context, otherwise // allocate one if (bContext == null) { bc = flcontext_alloc(); } else { bc = bContext; } if (eth != null) { IFloodlightProviderService.bcStore.put(bc, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, eth); } // Get the starting time (overall and per-component) of // the processing chain for this packet if performance // monitoring is turned on pktinProcTimeService.bootstrap(listeners); pktinProcTimeService.recordStartTimePktIn(); Command cmd; for (IOFMessageListener listener : listeners) { pktinProcTimeService.recordStartTimeComp(listener); cmd = listener.receive(sw, m, bc); pktinProcTimeService.recordEndTimeComp(listener); if (Command.STOP.equals(cmd)) { break; } } pktinProcTimeService.recordEndTimePktIn(sw, m, bc); } else { if (m.getType() != OFType.BARRIER_REPLY) log.warn("Unhandled OF Message: {} from {}", m, sw); else log.debug("Received a Barrier Reply, no listeners for it"); } // paag // And just before we exit the controller loop we see if anyone // is interested in knowing that we are exiting the loop for (IControllerCompletionListener listener : completionListeners) listener.onMessageConsumed(sw, m, bc); if ((bContext == null) && (bc != null)) flcontext_free(bc); } } // *************** // IFloodlightProvider // *************** /** * Forwards to RoleManager * @param ofSwitchHandshakeHandler * @param role */ void reassertRole(OFSwitchHandshakeHandler ofSwitchHandshakeHandler, HARole role) { roleManager.reassertRole(ofSwitchHandshakeHandler, role); } @Override public String getControllerId() { return controllerId; } @Override public Set<IPv4Address> getOFAddresses() { return Collections.unmodifiableSet(openFlowAddresses); } @Override public TransportPort getOFPort() { return openFlowPort; } // paag @Override public synchronized void addCompletionListener(IControllerCompletionListener listener) { completionListeners.add(listener); } //paag @Override public synchronized void removeCompletionListener(IControllerCompletionListener listener) { String listenerName = listener.getName(); if (completionListeners.remove(listener)) { log.debug("Removing completion listener {}", listenerName); } else { log.warn("Trying to remove unknown completion listener {}", listenerName); } listenerName = null; // help gc } @Override public synchronized void addOFMessageListener(OFType type, IOFMessageListener listener) { ListenerDispatcher<OFType, IOFMessageListener> ldd = messageListeners.get(type); if (ldd == null) { ldd = new ListenerDispatcher<OFType, IOFMessageListener>(); messageListeners.put(type, ldd); } ldd.addListener(type, listener); } @Override public synchronized void removeOFMessageListener(OFType type, IOFMessageListener listener) { ListenerDispatcher<OFType, IOFMessageListener> ldd = messageListeners.get(type); if (ldd != null) { ldd.removeListener(listener); } } private void logListeners() { for (Map.Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> entry : messageListeners .entrySet()) { OFType type = entry.getKey(); ListenerDispatcher<OFType, IOFMessageListener> ldd = entry.getValue(); StringBuilder sb = new StringBuilder(); sb.append("OFListeners for "); sb.append(type); sb.append(": "); for (IOFMessageListener l : ldd.getOrderedListeners()) { sb.append(l.getName()); sb.append(","); } log.debug(sb.toString()); } StringBuilder sb = new StringBuilder(); sb.append("HAListeners: "); for (IHAListener l : haListeners.getOrderedListeners()) { sb.append(l.getName()); sb.append(", "); } log.debug(sb.toString()); } public void removeOFMessageListeners(OFType type) { messageListeners.remove(type); } @Override public Map<OFType, List<IOFMessageListener>> getListeners() { Map<OFType, List<IOFMessageListener>> lers = new HashMap<OFType, List<IOFMessageListener>>(); for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e : messageListeners.entrySet()) { lers.put(e.getKey(), e.getValue().getOrderedListeners()); } return Collections.unmodifiableMap(lers); } @Override public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) { if (sw == null) throw new NullPointerException("Switch must not be null"); if (m == null) throw new NullPointerException("OFMessage must not be null"); FloodlightContext bc = new FloodlightContext(); List<IOFMessageListener> listeners = null; if (messageListeners.containsKey(m.getType())) { listeners = messageListeners.get(m.getType()).getOrderedListeners(); } if (listeners != null) { for (IOFMessageListener listener : listeners) { if (Command.STOP.equals(listener.receive(sw, m, bc))) { break; } } } } // ************** // Initialization // ************** /** * Sets the initial role based on properties in the config params. * It looks for two different properties. * If the "role" property is specified then the value should be * either "EQUAL", "MASTER", or "SLAVE" and the role of the * controller is set to the specified value. If the "role" property * is not specified then it looks next for the "role.path" property. * In this case the value should be the path to a property file in * the file system that contains a property called "floodlight.role" * which can be one of the values listed above for the "role" property. * The idea behind the "role.path" mechanism is that you have some * separate heartbeat and master controller election algorithm that * determines the role of the controller. When a role transition happens, * it updates the current role in the file specified by the "role.path" * file. Then if floodlight restarts for some reason it can get the * correct current role of the controller from the file. * @param configParams The config params for the FloodlightProvider service * @return A valid role if role information is specified in the * config params, otherwise null */ protected HARole getInitialRole(Map<String, String> configParams) { HARole role = HARole.STANDBY; String roleString = configParams.get("role"); if (roleString != null) { try { role = HARole.valueOfBackwardsCompatible(roleString); } catch (IllegalArgumentException exc) { log.error("Invalid current role value: {}", roleString); } } log.info("Controller role set to {}", role); return role; } /** * Tell controller that we're ready to accept switches loop * @throws IOException */ @Override public void run() { this.moduleLoaderState = ModuleLoaderState.COMPLETE; if (log.isDebugEnabled()) { logListeners(); } while (true) { try { IUpdate update = updates.take(); update.dispatch(); } catch (InterruptedException e) { log.error("Received interrupted exception in updates loop;" + "terminating process"); log.info("Calling System.exit"); System.exit(1); } catch (StorageException e) { log.error("Storage exception in controller " + "updates loop; terminating process", e); log.info("Calling System.exit"); System.exit(1); } catch (Exception e) { log.error("Exception in controller updates loop", e); } } } private void setConfigParams(Map<String, String> configParams) throws FloodlightModuleException { String ofPort = configParams.get("openFlowPort"); if (!Strings.isNullOrEmpty(ofPort)) { try { openFlowPort = TransportPort.of(Integer.parseInt(ofPort)); } catch (Exception e) { log.error("Invalid OpenFlow port {}, {}", ofPort, e); throw new FloodlightModuleException("Invalid OpenFlow port of " + ofPort + " in config"); } } log.info("OpenFlow port set to {}", openFlowPort); String threads = configParams.get("workerThreads"); if (!Strings.isNullOrEmpty(threads)) { this.workerThreads = Integer.parseInt(threads); } log.info("Number of worker threads set to {}", this.workerThreads); String addresses = configParams.get("openFlowAddresses"); if (!Strings.isNullOrEmpty(addresses)) { try { openFlowAddresses = Collections.singleton(IPv4Address.of(addresses)); //TODO support list of addresses for multi-honed controllers } catch (Exception e) { log.error("Invalid OpenFlow address {}, {}", addresses, e); throw new FloodlightModuleException("Invalid OpenFlow address of " + addresses + " in config"); } log.info("OpenFlow addresses set to {}", openFlowAddresses); } else { openFlowAddresses.add(IPv4Address.NONE); } } /** * Initialize internal data structures */ public void init(Map<String, String> configParams) throws FloodlightModuleException { this.moduleLoaderState = ModuleLoaderState.INIT; // These data structures are initialized here because other // module's startUp() might be called before ours this.messageListeners = new ConcurrentHashMap<OFType, ListenerDispatcher<OFType, IOFMessageListener>>(); this.haListeners = new ListenerDispatcher<HAListenerTypeMarker, IHAListener>(); this.controllerNodeIPsCache = new HashMap<String, String>(); this.updates = new LinkedBlockingQueue<IUpdate>(); this.providerMap = new HashMap<String, List<IInfoProvider>>(); this.completionListeners = new ConcurrentLinkedQueue<IControllerCompletionListener>(); setConfigParams(configParams); HARole initialRole = getInitialRole(configParams); this.notifiedRole = initialRole; this.shutdownService = new ShutdownServiceImpl(); this.roleManager = new RoleManager(this, this.shutdownService, this.notifiedRole, INITIAL_ROLE_CHANGE_DESCRIPTION); this.timer = new HashedWheelTimer(); // Switch Service Startup this.switchService.registerLogicalOFMessageCategory(LogicalOFMessageCategory.MAIN); this.switchService.addOFSwitchListener(new NotificationSwitchListener()); this.counters = new ControllerCounters(debugCounterService); } /** * Startup all of the controller's components * @param floodlightModuleLoader */ public void startupComponents(FloodlightModuleLoader floodlightModuleLoader) throws FloodlightModuleException { this.moduleLoaderState = ModuleLoaderState.STARTUP; // Create the table names we use storageSourceService.createTable(CONTROLLER_TABLE_NAME, null); storageSourceService.createTable(CONTROLLER_INTERFACE_TABLE_NAME, null); storageSourceService.createTable(SWITCH_CONFIG_TABLE_NAME, null); storageSourceService.setTablePrimaryKeyName(CONTROLLER_TABLE_NAME, CONTROLLER_ID); storageSourceService.addListener(CONTROLLER_INTERFACE_TABLE_NAME, this); storageSourceService.createTable(FLOW_PRIORITY_TABLE_NAME, null); storageSourceService.setTablePrimaryKeyName(FLOW_PRIORITY_TABLE_NAME, FLOW_COLUMN_PRIMARY_KEY); storageSourceService.addListener(FLOW_PRIORITY_TABLE_NAME, this); readFlowPriorityConfigurationFromStorage(); // // Startup load monitoring if (overload_drop) { this.loadmonitor.startMonitoring(this.threadPoolService.getScheduledExecutor()); } // Add our REST API restApiService.addRestletRoutable(new CoreWebRoutable()); try { this.syncService.registerStore(OFSwitchManager.SWITCH_SYNC_STORE_NAME, Scope.LOCAL); } catch (SyncException e) { throw new FloodlightModuleException("Error while setting up sync service", e); } addInfoProvider("summary", this); } private void readFlowPriorityConfigurationFromStorage() { try { Map<String, Object> row; IResultSet resultSet = storageSourceService.executeQuery(FLOW_PRIORITY_TABLE_NAME, FLOW_COLUMN_NAMES, null, null); if (resultSet == null) return; for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { row = it.next().getRow(); if (row.containsKey(FLOW_COLUMN_PRIMARY_KEY)) { String primary_key = (String) row.get(FLOW_COLUMN_PRIMARY_KEY); if (primary_key.equals(FLOW_VALUE_PRIMARY_KEY)) { if (row.containsKey(FLOW_COLUMN_ACCESS_PRIORITY)) { // Not used anymore DEFAULT_ACCESS_PRIORITY = Short.valueOf((String) row.get(FLOW_COLUMN_ACCESS_PRIORITY)); } if (row.containsKey(FLOW_COLUMN_CORE_PRIORITY)) { // Not used anymore DEFAULT_CORE_PRIORITY = Short.valueOf((String) row.get(FLOW_COLUMN_CORE_PRIORITY)); } } } } } catch (StorageException e) { log.error("Failed to access storage for forwarding configuration: {}", e.getMessage()); } catch (NumberFormatException e) { // log error, no stack-trace log.error("Failed to read core or access flow priority from " + "storage. Illegal number: {}", e.getMessage()); } } @Override public void addInfoProvider(String type, IInfoProvider provider) { if (!providerMap.containsKey(type)) { providerMap.put(type, new ArrayList<IInfoProvider>()); } providerMap.get(type).add(provider); } @Override public void removeInfoProvider(String type, IInfoProvider provider) { if (!providerMap.containsKey(type)) { log.debug("Provider type {} doesn't exist.", type); return; } providerMap.get(type).remove(provider); } @Override public Map<String, Object> getControllerInfo(String type) { if (!providerMap.containsKey(type)) return null; Map<String, Object> result = new LinkedHashMap<String, Object>(); for (IInfoProvider provider : providerMap.get(type)) { result.putAll(provider.getInfo(type)); } return result; } @Override public void addHAListener(IHAListener listener) { this.haListeners.addListener(null, listener); } @Override public void removeHAListener(IHAListener listener) { this.haListeners.removeListener(listener); } /** * Handle changes to the controller nodes IPs and dispatch update. */ protected void handleControllerNodeIPChanges() { HashMap<String, String> curControllerNodeIPs = new HashMap<String, String>(); HashMap<String, String> addedControllerNodeIPs = new HashMap<String, String>(); HashMap<String, String> removedControllerNodeIPs = new HashMap<String, String>(); String[] colNames = { CONTROLLER_INTERFACE_CONTROLLER_ID, CONTROLLER_INTERFACE_TYPE, CONTROLLER_INTERFACE_NUMBER, CONTROLLER_INTERFACE_DISCOVERED_IP }; synchronized (controllerNodeIPsCache) { // We currently assume that interface Ethernet0 is the relevant // controller interface. Might change. // We could (should?) implement this using // predicates, but creating the individual and compound predicate // seems more overhead then just checking every row. Particularly, // since the number of rows is small and changes infrequent IResultSet res = storageSourceService.executeQuery(CONTROLLER_INTERFACE_TABLE_NAME, colNames, null, null); while (res.next()) { if (res.getString(CONTROLLER_INTERFACE_TYPE).equals("Ethernet") && res.getInt(CONTROLLER_INTERFACE_NUMBER) == 0) { String controllerID = res.getString(CONTROLLER_INTERFACE_CONTROLLER_ID); String discoveredIP = res.getString(CONTROLLER_INTERFACE_DISCOVERED_IP); String curIP = controllerNodeIPsCache.get(controllerID); curControllerNodeIPs.put(controllerID, discoveredIP); if (curIP == null) { // new controller node IP addedControllerNodeIPs.put(controllerID, discoveredIP); } else if (!curIP.equals(discoveredIP)) { // IP changed removedControllerNodeIPs.put(controllerID, curIP); addedControllerNodeIPs.put(controllerID, discoveredIP); } } } // Now figure out if rows have been deleted. We can't use the // rowKeys from rowsDeleted directly, since the tables primary // key is a compound that we can't disassemble Set<String> curEntries = curControllerNodeIPs.keySet(); Set<String> removedEntries = controllerNodeIPsCache.keySet(); removedEntries.removeAll(curEntries); for (String removedControllerID : removedEntries) removedControllerNodeIPs.put(removedControllerID, controllerNodeIPsCache.get(removedControllerID)); controllerNodeIPsCache.clear(); controllerNodeIPsCache.putAll(curControllerNodeIPs); //counters.controllerNodeIpsChanged.updateCounterWithFlush(); HAControllerNodeIPUpdate update = new HAControllerNodeIPUpdate(curControllerNodeIPs, addedControllerNodeIPs, removedControllerNodeIPs); if (!removedControllerNodeIPs.isEmpty() || !addedControllerNodeIPs.isEmpty()) { addUpdateToQueue(update); } } } @Override public Map<String, String> getControllerNodeIPs() { // We return a copy of the mapping so we can guarantee that // the mapping return is the same as one that will be (or was) // dispatched to IHAListeners HashMap<String, String> retval = new HashMap<String, String>(); synchronized (controllerNodeIPsCache) { retval.putAll(controllerNodeIPsCache); } return retval; } private static final String FLOW_PRIORITY_CHANGED_AFTER_STARTUP = "Flow priority configuration has changed after " + "controller startup. Restart controller for new " + "configuration to take effect."; @Override public void rowsModified(String tableName, Set<Object> rowKeys) { if (tableName.equals(CONTROLLER_INTERFACE_TABLE_NAME)) { handleControllerNodeIPChanges(); } else if (tableName.equals(FLOW_PRIORITY_TABLE_NAME)) { log.warn(FLOW_PRIORITY_CHANGED_AFTER_STARTUP); } } @Override public void rowsDeleted(String tableName, Set<Object> rowKeys) { if (tableName.equals(CONTROLLER_INTERFACE_TABLE_NAME)) { handleControllerNodeIPChanges(); } else if (tableName.equals(FLOW_PRIORITY_TABLE_NAME)) { log.warn(FLOW_PRIORITY_CHANGED_AFTER_STARTUP); } } @Override public long getSystemStartTime() { RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); return rb.getStartTime(); } @Override public Map<String, Long> getMemory() { Map<String, Long> m = new HashMap<String, Long>(); Runtime runtime = Runtime.getRuntime(); m.put("total", runtime.totalMemory()); m.put("free", runtime.freeMemory()); return m; } @Override public Long getUptime() { RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); return rb.getUptime(); } @Override public void addUpdateToQueue(IUpdate update) { try { this.updates.put(update); } catch (InterruptedException e) { // This should never happen log.error("Failure adding update {} to queue.", update); } } /** * FOR TESTING ONLY. * Dispatch all updates in the update queue until queue is empty */ void processUpdateQueueForTesting() { while (!updates.isEmpty()) { IUpdate update = updates.poll(); if (update != null) update.dispatch(); } } /** * FOR TESTING ONLY * check if update queue is empty */ boolean isUpdateQueueEmptyForTesting() { return this.updates.isEmpty(); } /** * FOR TESTING ONLY */ void resetModuleState() { this.moduleLoaderState = ModuleLoaderState.INIT; } /** * FOR TESTING ONLY * sets module loader state */ void setModuleLoaderStateForTesting(ModuleLoaderState state) { this.moduleLoaderState = state; } @Override public Map<String, Object> getInfo(String type) { if (!"summary".equals(type)) return null; Map<String, Object> info = new HashMap<String, Object>(); info.put("# Switches", this.switchService.getAllSwitchDpids().size()); return info; } protected void setNotifiedRole(HARole newRole) { notifiedRole = newRole; } @Override public RoleManager getRoleManager() { return this.roleManager; } public Optional<ControllerId> getId() { short nodeId = this.syncService.getLocalNodeId(); if (nodeId == ClusterConfig.NODE_ID_UNCONFIGURED) return Optional.absent(); else return Optional.of(ControllerId.of(nodeId)); } @Override public Timer getTimer() { return this.timer; } public ControllerCounters getCounters() { return this.counters; } }